fix missing fields/meta data
[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                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052          }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059       
1060         // sort?? a.sort().equals(b.sort());
1061       
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064                 return false;
1065             }
1066         }
1067         return true;
1068     }
1069 });
1070
1071
1072  
1073 /*
1074  * Based on:
1075  * Ext JS Library 1.1.1
1076  * Copyright(c) 2006-2007, Ext JS, LLC.
1077  *
1078  * Originally Released Under LGPL - original licence link has changed is not relivant.
1079  *
1080  * Fork - LGPL
1081  * <script type="text/javascript">
1082  */
1083
1084 /**
1085  * @class Date
1086  *
1087  * The date parsing and format syntax is a subset of
1088  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1089  * supported will provide results equivalent to their PHP versions.
1090  *
1091  * Following is the list of all currently supported formats:
1092  *<pre>
1093 Sample date:
1094 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1095
1096 Format  Output      Description
1097 ------  ----------  --------------------------------------------------------------
1098   d      10         Day of the month, 2 digits with leading zeros
1099   D      Wed        A textual representation of a day, three letters
1100   j      10         Day of the month without leading zeros
1101   l      Wednesday  A full textual representation of the day of the week
1102   S      th         English ordinal day of month suffix, 2 chars (use with j)
1103   w      3          Numeric representation of the day of the week
1104   z      9          The julian date, or day of the year (0-365)
1105   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1106   F      January    A full textual representation of the month
1107   m      01         Numeric representation of a month, with leading zeros
1108   M      Jan        Month name abbreviation, three letters
1109   n      1          Numeric representation of a month, without leading zeros
1110   t      31         Number of days in the given month
1111   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1112   Y      2007       A full numeric representation of a year, 4 digits
1113   y      07         A two digit representation of a year
1114   a      pm         Lowercase Ante meridiem and Post meridiem
1115   A      PM         Uppercase Ante meridiem and Post meridiem
1116   g      3          12-hour format of an hour without leading zeros
1117   G      15         24-hour format of an hour without leading zeros
1118   h      03         12-hour format of an hour with leading zeros
1119   H      15         24-hour format of an hour with leading zeros
1120   i      05         Minutes with leading zeros
1121   s      01         Seconds, with leading zeros
1122   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1123   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1124   T      CST        Timezone setting of the machine running the code
1125   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1126 </pre>
1127  *
1128  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1129  * <pre><code>
1130 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1131 document.write(dt.format('Y-m-d'));                         //2007-01-10
1132 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1133 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
1134  </code></pre>
1135  *
1136  * Here are some standard date/time patterns that you might find helpful.  They
1137  * are not part of the source of Date.js, but to use them you can simply copy this
1138  * block of code into any script that is included after Date.js and they will also become
1139  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1140  * <pre><code>
1141 Date.patterns = {
1142     ISO8601Long:"Y-m-d H:i:s",
1143     ISO8601Short:"Y-m-d",
1144     ShortDate: "n/j/Y",
1145     LongDate: "l, F d, Y",
1146     FullDateTime: "l, F d, Y g:i:s A",
1147     MonthDay: "F d",
1148     ShortTime: "g:i A",
1149     LongTime: "g:i:s A",
1150     SortableDateTime: "Y-m-d\\TH:i:s",
1151     UniversalSortableDateTime: "Y-m-d H:i:sO",
1152     YearMonth: "F, Y"
1153 };
1154 </code></pre>
1155  *
1156  * Example usage:
1157  * <pre><code>
1158 var dt = new Date();
1159 document.write(dt.format(Date.patterns.ShortDate));
1160  </code></pre>
1161  */
1162
1163 /*
1164  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1165  * They generate precompiled functions from date formats instead of parsing and
1166  * processing the pattern every time you format a date.  These functions are available
1167  * on every Date object (any javascript function).
1168  *
1169  * The original article and download are here:
1170  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1171  *
1172  */
1173  
1174  
1175  // was in core
1176 /**
1177  Returns the number of milliseconds between this date and date
1178  @param {Date} date (optional) Defaults to now
1179  @return {Number} The diff in milliseconds
1180  @member Date getElapsed
1181  */
1182 Date.prototype.getElapsed = function(date) {
1183         return Math.abs((date || new Date()).getTime()-this.getTime());
1184 };
1185 // was in date file..
1186
1187
1188 // private
1189 Date.parseFunctions = {count:0};
1190 // private
1191 Date.parseRegexes = [];
1192 // private
1193 Date.formatFunctions = {count:0};
1194
1195 // private
1196 Date.prototype.dateFormat = function(format) {
1197     if (Date.formatFunctions[format] == null) {
1198         Date.createNewFormat(format);
1199     }
1200     var func = Date.formatFunctions[format];
1201     return this[func]();
1202 };
1203
1204
1205 /**
1206  * Formats a date given the supplied format string
1207  * @param {String} format The format string
1208  * @return {String} The formatted date
1209  * @method
1210  */
1211 Date.prototype.format = Date.prototype.dateFormat;
1212
1213 // private
1214 Date.createNewFormat = function(format) {
1215     var funcName = "format" + Date.formatFunctions.count++;
1216     Date.formatFunctions[format] = funcName;
1217     var code = "Date.prototype." + funcName + " = function(){return ";
1218     var special = false;
1219     var ch = '';
1220     for (var i = 0; i < format.length; ++i) {
1221         ch = format.charAt(i);
1222         if (!special && ch == "\\") {
1223             special = true;
1224         }
1225         else if (special) {
1226             special = false;
1227             code += "'" + String.escape(ch) + "' + ";
1228         }
1229         else {
1230             code += Date.getFormatCode(ch);
1231         }
1232     }
1233     /** eval:var:zzzzzzzzzzzzz */
1234     eval(code.substring(0, code.length - 3) + ";}");
1235 };
1236
1237 // private
1238 Date.getFormatCode = function(character) {
1239     switch (character) {
1240     case "d":
1241         return "String.leftPad(this.getDate(), 2, '0') + ";
1242     case "D":
1243         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1244     case "j":
1245         return "this.getDate() + ";
1246     case "l":
1247         return "Date.dayNames[this.getDay()] + ";
1248     case "S":
1249         return "this.getSuffix() + ";
1250     case "w":
1251         return "this.getDay() + ";
1252     case "z":
1253         return "this.getDayOfYear() + ";
1254     case "W":
1255         return "this.getWeekOfYear() + ";
1256     case "F":
1257         return "Date.monthNames[this.getMonth()] + ";
1258     case "m":
1259         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1260     case "M":
1261         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1262     case "n":
1263         return "(this.getMonth() + 1) + ";
1264     case "t":
1265         return "this.getDaysInMonth() + ";
1266     case "L":
1267         return "(this.isLeapYear() ? 1 : 0) + ";
1268     case "Y":
1269         return "this.getFullYear() + ";
1270     case "y":
1271         return "('' + this.getFullYear()).substring(2, 4) + ";
1272     case "a":
1273         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1274     case "A":
1275         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1276     case "g":
1277         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1278     case "G":
1279         return "this.getHours() + ";
1280     case "h":
1281         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1282     case "H":
1283         return "String.leftPad(this.getHours(), 2, '0') + ";
1284     case "i":
1285         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1286     case "s":
1287         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1288     case "O":
1289         return "this.getGMTOffset() + ";
1290     case "P":
1291         return "this.getGMTColonOffset() + ";
1292     case "T":
1293         return "this.getTimezone() + ";
1294     case "Z":
1295         return "(this.getTimezoneOffset() * -60) + ";
1296     default:
1297         return "'" + String.escape(character) + "' + ";
1298     }
1299 };
1300
1301 /**
1302  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1303  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1304  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1305  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1306  * string or the parse operation will fail.
1307  * Example Usage:
1308 <pre><code>
1309 //dt = Fri May 25 2007 (current date)
1310 var dt = new Date();
1311
1312 //dt = Thu May 25 2006 (today's month/day in 2006)
1313 dt = Date.parseDate("2006", "Y");
1314
1315 //dt = Sun Jan 15 2006 (all date parts specified)
1316 dt = Date.parseDate("2006-1-15", "Y-m-d");
1317
1318 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1319 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1320 </code></pre>
1321  * @param {String} input The unparsed date as a string
1322  * @param {String} format The format the date is in
1323  * @return {Date} The parsed date
1324  * @static
1325  */
1326 Date.parseDate = function(input, format) {
1327     if (Date.parseFunctions[format] == null) {
1328         Date.createParser(format);
1329     }
1330     var func = Date.parseFunctions[format];
1331     return Date[func](input);
1332 };
1333 /**
1334  * @private
1335  */
1336
1337 Date.createParser = function(format) {
1338     var funcName = "parse" + Date.parseFunctions.count++;
1339     var regexNum = Date.parseRegexes.length;
1340     var currentGroup = 1;
1341     Date.parseFunctions[format] = funcName;
1342
1343     var code = "Date." + funcName + " = function(input){\n"
1344         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1345         + "var d = new Date();\n"
1346         + "y = d.getFullYear();\n"
1347         + "m = d.getMonth();\n"
1348         + "d = d.getDate();\n"
1349         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1350         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1351         + "if (results && results.length > 0) {";
1352     var regex = "";
1353
1354     var special = false;
1355     var ch = '';
1356     for (var i = 0; i < format.length; ++i) {
1357         ch = format.charAt(i);
1358         if (!special && ch == "\\") {
1359             special = true;
1360         }
1361         else if (special) {
1362             special = false;
1363             regex += String.escape(ch);
1364         }
1365         else {
1366             var obj = Date.formatCodeToRegex(ch, currentGroup);
1367             currentGroup += obj.g;
1368             regex += obj.s;
1369             if (obj.g && obj.c) {
1370                 code += obj.c;
1371             }
1372         }
1373     }
1374
1375     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1376         + "{v = new Date(y, m, d, h, i, s);}\n"
1377         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1378         + "{v = new Date(y, m, d, h, i);}\n"
1379         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1380         + "{v = new Date(y, m, d, h);}\n"
1381         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1382         + "{v = new Date(y, m, d);}\n"
1383         + "else if (y >= 0 && m >= 0)\n"
1384         + "{v = new Date(y, m);}\n"
1385         + "else if (y >= 0)\n"
1386         + "{v = new Date(y);}\n"
1387         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1388         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1389         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1390         + ";}";
1391
1392     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1393     /** eval:var:zzzzzzzzzzzzz */
1394     eval(code);
1395 };
1396
1397 // private
1398 Date.formatCodeToRegex = function(character, currentGroup) {
1399     switch (character) {
1400     case "D":
1401         return {g:0,
1402         c:null,
1403         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1404     case "j":
1405         return {g:1,
1406             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1407             s:"(\\d{1,2})"}; // day of month without leading zeroes
1408     case "d":
1409         return {g:1,
1410             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1411             s:"(\\d{2})"}; // day of month with leading zeroes
1412     case "l":
1413         return {g:0,
1414             c:null,
1415             s:"(?:" + Date.dayNames.join("|") + ")"};
1416     case "S":
1417         return {g:0,
1418             c:null,
1419             s:"(?:st|nd|rd|th)"};
1420     case "w":
1421         return {g:0,
1422             c:null,
1423             s:"\\d"};
1424     case "z":
1425         return {g:0,
1426             c:null,
1427             s:"(?:\\d{1,3})"};
1428     case "W":
1429         return {g:0,
1430             c:null,
1431             s:"(?:\\d{2})"};
1432     case "F":
1433         return {g:1,
1434             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1435             s:"(" + Date.monthNames.join("|") + ")"};
1436     case "M":
1437         return {g:1,
1438             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1439             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1440     case "n":
1441         return {g:1,
1442             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1443             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1444     case "m":
1445         return {g:1,
1446             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1447             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1448     case "t":
1449         return {g:0,
1450             c:null,
1451             s:"\\d{1,2}"};
1452     case "L":
1453         return {g:0,
1454             c:null,
1455             s:"(?:1|0)"};
1456     case "Y":
1457         return {g:1,
1458             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1459             s:"(\\d{4})"};
1460     case "y":
1461         return {g:1,
1462             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1463                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1464             s:"(\\d{1,2})"};
1465     case "a":
1466         return {g:1,
1467             c:"if (results[" + currentGroup + "] == 'am') {\n"
1468                 + "if (h == 12) { h = 0; }\n"
1469                 + "} else { if (h < 12) { h += 12; }}",
1470             s:"(am|pm)"};
1471     case "A":
1472         return {g:1,
1473             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1474                 + "if (h == 12) { h = 0; }\n"
1475                 + "} else { if (h < 12) { h += 12; }}",
1476             s:"(AM|PM)"};
1477     case "g":
1478     case "G":
1479         return {g:1,
1480             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1481             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1482     case "h":
1483     case "H":
1484         return {g:1,
1485             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1486             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1487     case "i":
1488         return {g:1,
1489             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1490             s:"(\\d{2})"};
1491     case "s":
1492         return {g:1,
1493             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1494             s:"(\\d{2})"};
1495     case "O":
1496         return {g:1,
1497             c:[
1498                 "o = results[", currentGroup, "];\n",
1499                 "var sn = o.substring(0,1);\n", // get + / - sign
1500                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1501                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1502                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1503                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1504             ].join(""),
1505             s:"([+\-]\\d{2,4})"};
1506     
1507     
1508     case "P":
1509         return {g:1,
1510                 c:[
1511                    "o = results[", currentGroup, "];\n",
1512                    "var sn = o.substring(0,1);\n",
1513                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1514                    "var mn = o.substring(4,6) % 60;\n",
1515                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1516                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1517             ].join(""),
1518             s:"([+\-]\\d{4})"};
1519     case "T":
1520         return {g:0,
1521             c:null,
1522             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1523     case "Z":
1524         return {g:1,
1525             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1526                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1527             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1528     default:
1529         return {g:0,
1530             c:null,
1531             s:String.escape(character)};
1532     }
1533 };
1534
1535 /**
1536  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1537  * @return {String} The abbreviated timezone name (e.g. 'CST')
1538  */
1539 Date.prototype.getTimezone = function() {
1540     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1541 };
1542
1543 /**
1544  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1545  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1546  */
1547 Date.prototype.getGMTOffset = function() {
1548     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1549         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1550         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1551 };
1552
1553 /**
1554  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1555  * @return {String} 2-characters representing hours and 2-characters representing minutes
1556  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1557  */
1558 Date.prototype.getGMTColonOffset = function() {
1559         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1560                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1561                 + ":"
1562                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1563 }
1564
1565 /**
1566  * Get the numeric day number of the year, adjusted for leap year.
1567  * @return {Number} 0 through 364 (365 in leap years)
1568  */
1569 Date.prototype.getDayOfYear = function() {
1570     var num = 0;
1571     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1572     for (var i = 0; i < this.getMonth(); ++i) {
1573         num += Date.daysInMonth[i];
1574     }
1575     return num + this.getDate() - 1;
1576 };
1577
1578 /**
1579  * Get the string representation of the numeric week number of the year
1580  * (equivalent to the format specifier 'W').
1581  * @return {String} '00' through '52'
1582  */
1583 Date.prototype.getWeekOfYear = function() {
1584     // Skip to Thursday of this week
1585     var now = this.getDayOfYear() + (4 - this.getDay());
1586     // Find the first Thursday of the year
1587     var jan1 = new Date(this.getFullYear(), 0, 1);
1588     var then = (7 - jan1.getDay() + 4);
1589     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1590 };
1591
1592 /**
1593  * Whether or not the current date is in a leap year.
1594  * @return {Boolean} True if the current date is in a leap year, else false
1595  */
1596 Date.prototype.isLeapYear = function() {
1597     var year = this.getFullYear();
1598     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1599 };
1600
1601 /**
1602  * Get the first day of the current month, adjusted for leap year.  The returned value
1603  * is the numeric day index within the week (0-6) which can be used in conjunction with
1604  * the {@link #monthNames} array to retrieve the textual day name.
1605  * Example:
1606  *<pre><code>
1607 var dt = new Date('1/10/2007');
1608 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1609 </code></pre>
1610  * @return {Number} The day number (0-6)
1611  */
1612 Date.prototype.getFirstDayOfMonth = function() {
1613     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1614     return (day < 0) ? (day + 7) : day;
1615 };
1616
1617 /**
1618  * Get the last day of the current month, adjusted for leap year.  The returned value
1619  * is the numeric day index within the week (0-6) which can be used in conjunction with
1620  * the {@link #monthNames} array to retrieve the textual day name.
1621  * Example:
1622  *<pre><code>
1623 var dt = new Date('1/10/2007');
1624 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1625 </code></pre>
1626  * @return {Number} The day number (0-6)
1627  */
1628 Date.prototype.getLastDayOfMonth = function() {
1629     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1630     return (day < 0) ? (day + 7) : day;
1631 };
1632
1633
1634 /**
1635  * Get the first date of this date's month
1636  * @return {Date}
1637  */
1638 Date.prototype.getFirstDateOfMonth = function() {
1639     return new Date(this.getFullYear(), this.getMonth(), 1);
1640 };
1641
1642 /**
1643  * Get the last date of this date's month
1644  * @return {Date}
1645  */
1646 Date.prototype.getLastDateOfMonth = function() {
1647     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1648 };
1649 /**
1650  * Get the number of days in the current month, adjusted for leap year.
1651  * @return {Number} The number of days in the month
1652  */
1653 Date.prototype.getDaysInMonth = function() {
1654     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1655     return Date.daysInMonth[this.getMonth()];
1656 };
1657
1658 /**
1659  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1660  * @return {String} 'st, 'nd', 'rd' or 'th'
1661  */
1662 Date.prototype.getSuffix = function() {
1663     switch (this.getDate()) {
1664         case 1:
1665         case 21:
1666         case 31:
1667             return "st";
1668         case 2:
1669         case 22:
1670             return "nd";
1671         case 3:
1672         case 23:
1673             return "rd";
1674         default:
1675             return "th";
1676     }
1677 };
1678
1679 // private
1680 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1681
1682 /**
1683  * An array of textual month names.
1684  * Override these values for international dates, for example...
1685  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1686  * @type Array
1687  * @static
1688  */
1689 Date.monthNames =
1690    ["January",
1691     "February",
1692     "March",
1693     "April",
1694     "May",
1695     "June",
1696     "July",
1697     "August",
1698     "September",
1699     "October",
1700     "November",
1701     "December"];
1702
1703 /**
1704  * An array of textual day names.
1705  * Override these values for international dates, for example...
1706  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1707  * @type Array
1708  * @static
1709  */
1710 Date.dayNames =
1711    ["Sunday",
1712     "Monday",
1713     "Tuesday",
1714     "Wednesday",
1715     "Thursday",
1716     "Friday",
1717     "Saturday"];
1718
1719 // private
1720 Date.y2kYear = 50;
1721 // private
1722 Date.monthNumbers = {
1723     Jan:0,
1724     Feb:1,
1725     Mar:2,
1726     Apr:3,
1727     May:4,
1728     Jun:5,
1729     Jul:6,
1730     Aug:7,
1731     Sep:8,
1732     Oct:9,
1733     Nov:10,
1734     Dec:11};
1735
1736 /**
1737  * Creates and returns a new Date instance with the exact same date value as the called instance.
1738  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1739  * variable will also be changed.  When the intention is to create a new variable that will not
1740  * modify the original instance, you should create a clone.
1741  *
1742  * Example of correctly cloning a date:
1743  * <pre><code>
1744 //wrong way:
1745 var orig = new Date('10/1/2006');
1746 var copy = orig;
1747 copy.setDate(5);
1748 document.write(orig);  //returns 'Thu Oct 05 2006'!
1749
1750 //correct way:
1751 var orig = new Date('10/1/2006');
1752 var copy = orig.clone();
1753 copy.setDate(5);
1754 document.write(orig);  //returns 'Thu Oct 01 2006'
1755 </code></pre>
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.clone = function() {
1759         return new Date(this.getTime());
1760 };
1761
1762 /**
1763  * Clears any time information from this date
1764  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1765  @return {Date} this or the clone
1766  */
1767 Date.prototype.clearTime = function(clone){
1768     if(clone){
1769         return this.clone().clearTime();
1770     }
1771     this.setHours(0);
1772     this.setMinutes(0);
1773     this.setSeconds(0);
1774     this.setMilliseconds(0);
1775     return this;
1776 };
1777
1778 // private
1779 // safari setMonth is broken -- check that this is only donw once...
1780 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1781     Date.brokenSetMonth = Date.prototype.setMonth;
1782         Date.prototype.setMonth = function(num){
1783                 if(num <= -1){
1784                         var n = Math.ceil(-num);
1785                         var back_year = Math.ceil(n/12);
1786                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1787                         this.setFullYear(this.getFullYear() - back_year);
1788                         return Date.brokenSetMonth.call(this, month);
1789                 } else {
1790                         return Date.brokenSetMonth.apply(this, arguments);
1791                 }
1792         };
1793 }
1794
1795 /** Date interval constant 
1796 * @static 
1797 * @type String */
1798 Date.MILLI = "ms";
1799 /** Date interval constant 
1800 * @static 
1801 * @type String */
1802 Date.SECOND = "s";
1803 /** Date interval constant 
1804 * @static 
1805 * @type String */
1806 Date.MINUTE = "mi";
1807 /** Date interval constant 
1808 * @static 
1809 * @type String */
1810 Date.HOUR = "h";
1811 /** Date interval constant 
1812 * @static 
1813 * @type String */
1814 Date.DAY = "d";
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MONTH = "mo";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.YEAR = "y";
1823
1824 /**
1825  * Provides a convenient method of performing basic date arithmetic.  This method
1826  * does not modify the Date instance being called - it creates and returns
1827  * a new Date instance containing the resulting date value.
1828  *
1829  * Examples:
1830  * <pre><code>
1831 //Basic usage:
1832 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1833 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1834
1835 //Negative values will subtract correctly:
1836 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1837 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1838
1839 //You can even chain several calls together in one line!
1840 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1841 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1842  </code></pre>
1843  *
1844  * @param {String} interval   A valid date interval enum value
1845  * @param {Number} value      The amount to add to the current date
1846  * @return {Date} The new Date instance
1847  */
1848 Date.prototype.add = function(interval, value){
1849   var d = this.clone();
1850   if (!interval || value === 0) { return d; }
1851   switch(interval.toLowerCase()){
1852     case Date.MILLI:
1853       d.setMilliseconds(this.getMilliseconds() + value);
1854       break;
1855     case Date.SECOND:
1856       d.setSeconds(this.getSeconds() + value);
1857       break;
1858     case Date.MINUTE:
1859       d.setMinutes(this.getMinutes() + value);
1860       break;
1861     case Date.HOUR:
1862       d.setHours(this.getHours() + value);
1863       break;
1864     case Date.DAY:
1865       d.setDate(this.getDate() + value);
1866       break;
1867     case Date.MONTH:
1868       var day = this.getDate();
1869       if(day > 28){
1870           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1871       }
1872       d.setDate(day);
1873       d.setMonth(this.getMonth() + value);
1874       break;
1875     case Date.YEAR:
1876       d.setFullYear(this.getFullYear() + value);
1877       break;
1878   }
1879   return d;
1880 };
1881 /**
1882  * @class Roo.lib.Dom
1883  * @licence LGPL
1884  * @static
1885  * 
1886  * Dom utils (from YIU afaik)
1887  *
1888  * 
1889  **/
1890 Roo.lib.Dom = {
1891     /**
1892      * Get the view width
1893      * @param {Boolean} full True will get the full document, otherwise it's the view width
1894      * @return {Number} The width
1895      */
1896      
1897     getViewWidth : function(full) {
1898         return full ? this.getDocumentWidth() : this.getViewportWidth();
1899     },
1900     /**
1901      * Get the view height
1902      * @param {Boolean} full True will get the full document, otherwise it's the view height
1903      * @return {Number} The height
1904      */
1905     getViewHeight : function(full) {
1906         return full ? this.getDocumentHeight() : this.getViewportHeight();
1907     },
1908     /**
1909      * Get the Full Document height 
1910      * @return {Number} The height
1911      */
1912     getDocumentHeight: function() {
1913         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1914         return Math.max(scrollHeight, this.getViewportHeight());
1915     },
1916     /**
1917      * Get the Full Document width
1918      * @return {Number} The width
1919      */
1920     getDocumentWidth: function() {
1921         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1922         return Math.max(scrollWidth, this.getViewportWidth());
1923     },
1924     /**
1925      * Get the Window Viewport height
1926      * @return {Number} The height
1927      */
1928     getViewportHeight: function() {
1929         var height = self.innerHeight;
1930         var mode = document.compatMode;
1931
1932         if ((mode || Roo.isIE) && !Roo.isOpera) {
1933             height = (mode == "CSS1Compat") ?
1934                      document.documentElement.clientHeight :
1935                      document.body.clientHeight;
1936         }
1937
1938         return height;
1939     },
1940     /**
1941      * Get the Window Viewport width
1942      * @return {Number} The width
1943      */
1944     getViewportWidth: function() {
1945         var width = self.innerWidth;
1946         var mode = document.compatMode;
1947
1948         if (mode || Roo.isIE) {
1949             width = (mode == "CSS1Compat") ?
1950                     document.documentElement.clientWidth :
1951                     document.body.clientWidth;
1952         }
1953         return width;
1954     },
1955
1956     isAncestor : function(p, c) {
1957         p = Roo.getDom(p);
1958         c = Roo.getDom(c);
1959         if (!p || !c) {
1960             return false;
1961         }
1962
1963         if (p.contains && !Roo.isSafari) {
1964             return p.contains(c);
1965         } else if (p.compareDocumentPosition) {
1966             return !!(p.compareDocumentPosition(c) & 16);
1967         } else {
1968             var parent = c.parentNode;
1969             while (parent) {
1970                 if (parent == p) {
1971                     return true;
1972                 }
1973                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1974                     return false;
1975                 }
1976                 parent = parent.parentNode;
1977             }
1978             return false;
1979         }
1980     },
1981
1982     getRegion : function(el) {
1983         return Roo.lib.Region.getRegion(el);
1984     },
1985
1986     getY : function(el) {
1987         return this.getXY(el)[1];
1988     },
1989
1990     getX : function(el) {
1991         return this.getXY(el)[0];
1992     },
1993
1994     getXY : function(el) {
1995         var p, pe, b, scroll, bd = document.body;
1996         el = Roo.getDom(el);
1997         var fly = Roo.lib.AnimBase.fly;
1998         if (el.getBoundingClientRect) {
1999             b = el.getBoundingClientRect();
2000             scroll = fly(document).getScroll();
2001             return [b.left + scroll.left, b.top + scroll.top];
2002         }
2003         var x = 0, y = 0;
2004
2005         p = el;
2006
2007         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2008
2009         while (p) {
2010
2011             x += p.offsetLeft;
2012             y += p.offsetTop;
2013
2014             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2015                 hasAbsolute = true;
2016             }
2017
2018             if (Roo.isGecko) {
2019                 pe = fly(p);
2020
2021                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2022                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2023
2024
2025                 x += bl;
2026                 y += bt;
2027
2028
2029                 if (p != el && pe.getStyle('overflow') != 'visible') {
2030                     x += bl;
2031                     y += bt;
2032                 }
2033             }
2034             p = p.offsetParent;
2035         }
2036
2037         if (Roo.isSafari && hasAbsolute) {
2038             x -= bd.offsetLeft;
2039             y -= bd.offsetTop;
2040         }
2041
2042         if (Roo.isGecko && !hasAbsolute) {
2043             var dbd = fly(bd);
2044             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2045             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2046         }
2047
2048         p = el.parentNode;
2049         while (p && p != bd) {
2050             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2051                 x -= p.scrollLeft;
2052                 y -= p.scrollTop;
2053             }
2054             p = p.parentNode;
2055         }
2056         return [x, y];
2057     },
2058  
2059   
2060
2061
2062     setXY : function(el, xy) {
2063         el = Roo.fly(el, '_setXY');
2064         el.position();
2065         var pts = el.translatePoints(xy);
2066         if (xy[0] !== false) {
2067             el.dom.style.left = pts.left + "px";
2068         }
2069         if (xy[1] !== false) {
2070             el.dom.style.top = pts.top + "px";
2071         }
2072     },
2073
2074     setX : function(el, x) {
2075         this.setXY(el, [x, false]);
2076     },
2077
2078     setY : function(el, y) {
2079         this.setXY(el, [false, y]);
2080     }
2081 };
2082 /*
2083  * Portions of this file are based on pieces of Yahoo User Interface Library
2084  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2085  * YUI licensed under the BSD License:
2086  * http://developer.yahoo.net/yui/license.txt
2087  * <script type="text/javascript">
2088  *
2089  */
2090
2091 Roo.lib.Event = function() {
2092     var loadComplete = false;
2093     var listeners = [];
2094     var unloadListeners = [];
2095     var retryCount = 0;
2096     var onAvailStack = [];
2097     var counter = 0;
2098     var lastError = null;
2099
2100     return {
2101         POLL_RETRYS: 200,
2102         POLL_INTERVAL: 20,
2103         EL: 0,
2104         TYPE: 1,
2105         FN: 2,
2106         WFN: 3,
2107         OBJ: 3,
2108         ADJ_SCOPE: 4,
2109         _interval: null,
2110
2111         startInterval: function() {
2112             if (!this._interval) {
2113                 var self = this;
2114                 var callback = function() {
2115                     self._tryPreloadAttach();
2116                 };
2117                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2118
2119             }
2120         },
2121
2122         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2123             onAvailStack.push({ id:         p_id,
2124                 fn:         p_fn,
2125                 obj:        p_obj,
2126                 override:   p_override,
2127                 checkReady: false    });
2128
2129             retryCount = this.POLL_RETRYS;
2130             this.startInterval();
2131         },
2132
2133
2134         addListener: function(el, eventName, fn) {
2135             el = Roo.getDom(el);
2136             if (!el || !fn) {
2137                 return false;
2138             }
2139
2140             if ("unload" == eventName) {
2141                 unloadListeners[unloadListeners.length] =
2142                 [el, eventName, fn];
2143                 return true;
2144             }
2145
2146             var wrappedFn = function(e) {
2147                 return fn(Roo.lib.Event.getEvent(e));
2148             };
2149
2150             var li = [el, eventName, fn, wrappedFn];
2151
2152             var index = listeners.length;
2153             listeners[index] = li;
2154
2155             this.doAdd(el, eventName, wrappedFn, false);
2156             return true;
2157
2158         },
2159
2160
2161         removeListener: function(el, eventName, fn) {
2162             var i, len;
2163
2164             el = Roo.getDom(el);
2165
2166             if(!fn) {
2167                 return this.purgeElement(el, false, eventName);
2168             }
2169
2170
2171             if ("unload" == eventName) {
2172
2173                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2174                     var li = unloadListeners[i];
2175                     if (li &&
2176                         li[0] == el &&
2177                         li[1] == eventName &&
2178                         li[2] == fn) {
2179                         unloadListeners.splice(i, 1);
2180                         return true;
2181                     }
2182                 }
2183
2184                 return false;
2185             }
2186
2187             var cacheItem = null;
2188
2189
2190             var index = arguments[3];
2191
2192             if ("undefined" == typeof index) {
2193                 index = this._getCacheIndex(el, eventName, fn);
2194             }
2195
2196             if (index >= 0) {
2197                 cacheItem = listeners[index];
2198             }
2199
2200             if (!el || !cacheItem) {
2201                 return false;
2202             }
2203
2204             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2205
2206             delete listeners[index][this.WFN];
2207             delete listeners[index][this.FN];
2208             listeners.splice(index, 1);
2209
2210             return true;
2211
2212         },
2213
2214
2215         getTarget: function(ev, resolveTextNode) {
2216             ev = ev.browserEvent || ev;
2217             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2218             var t = ev.target || ev.srcElement;
2219             return this.resolveTextNode(t);
2220         },
2221
2222
2223         resolveTextNode: function(node) {
2224             if (Roo.isSafari && node && 3 == node.nodeType) {
2225                 return node.parentNode;
2226             } else {
2227                 return node;
2228             }
2229         },
2230
2231
2232         getPageX: function(ev) {
2233             ev = ev.browserEvent || ev;
2234             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2235             var x = ev.pageX;
2236             if (!x && 0 !== x) {
2237                 x = ev.clientX || 0;
2238
2239                 if (Roo.isIE) {
2240                     x += this.getScroll()[1];
2241                 }
2242             }
2243
2244             return x;
2245         },
2246
2247
2248         getPageY: function(ev) {
2249             ev = ev.browserEvent || ev;
2250             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2251             var y = ev.pageY;
2252             if (!y && 0 !== y) {
2253                 y = ev.clientY || 0;
2254
2255                 if (Roo.isIE) {
2256                     y += this.getScroll()[0];
2257                 }
2258             }
2259
2260
2261             return y;
2262         },
2263
2264
2265         getXY: function(ev) {
2266             ev = ev.browserEvent || ev;
2267             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2268             return [this.getPageX(ev), this.getPageY(ev)];
2269         },
2270
2271
2272         getRelatedTarget: function(ev) {
2273             ev = ev.browserEvent || ev;
2274             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2275             var t = ev.relatedTarget;
2276             if (!t) {
2277                 if (ev.type == "mouseout") {
2278                     t = ev.toElement;
2279                 } else if (ev.type == "mouseover") {
2280                     t = ev.fromElement;
2281                 }
2282             }
2283
2284             return this.resolveTextNode(t);
2285         },
2286
2287
2288         getTime: function(ev) {
2289             ev = ev.browserEvent || ev;
2290             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2291             if (!ev.time) {
2292                 var t = new Date().getTime();
2293                 try {
2294                     ev.time = t;
2295                 } catch(ex) {
2296                     this.lastError = ex;
2297                     return t;
2298                 }
2299             }
2300
2301             return ev.time;
2302         },
2303
2304
2305         stopEvent: function(ev) {
2306             this.stopPropagation(ev);
2307             this.preventDefault(ev);
2308         },
2309
2310
2311         stopPropagation: function(ev) {
2312             ev = ev.browserEvent || ev;
2313             if (ev.stopPropagation) {
2314                 ev.stopPropagation();
2315             } else {
2316                 ev.cancelBubble = true;
2317             }
2318         },
2319
2320
2321         preventDefault: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             if(ev.preventDefault) {
2324                 ev.preventDefault();
2325             } else {
2326                 ev.returnValue = false;
2327             }
2328         },
2329
2330
2331         getEvent: function(e) {
2332             var ev = e || window.event;
2333             if (!ev) {
2334                 var c = this.getEvent.caller;
2335                 while (c) {
2336                     ev = c.arguments[0];
2337                     if (ev && Event == ev.constructor) {
2338                         break;
2339                     }
2340                     c = c.caller;
2341                 }
2342             }
2343             return ev;
2344         },
2345
2346
2347         getCharCode: function(ev) {
2348             ev = ev.browserEvent || ev;
2349             return ev.charCode || ev.keyCode || 0;
2350         },
2351
2352
2353         _getCacheIndex: function(el, eventName, fn) {
2354             for (var i = 0,len = listeners.length; i < len; ++i) {
2355                 var li = listeners[i];
2356                 if (li &&
2357                     li[this.FN] == fn &&
2358                     li[this.EL] == el &&
2359                     li[this.TYPE] == eventName) {
2360                     return i;
2361                 }
2362             }
2363
2364             return -1;
2365         },
2366
2367
2368         elCache: {},
2369
2370
2371         getEl: function(id) {
2372             return document.getElementById(id);
2373         },
2374
2375
2376         clearCache: function() {
2377         },
2378
2379
2380         _load: function(e) {
2381             loadComplete = true;
2382             var EU = Roo.lib.Event;
2383
2384
2385             if (Roo.isIE) {
2386                 EU.doRemove(window, "load", EU._load);
2387             }
2388         },
2389
2390
2391         _tryPreloadAttach: function() {
2392
2393             if (this.locked) {
2394                 return false;
2395             }
2396
2397             this.locked = true;
2398
2399
2400             var tryAgain = !loadComplete;
2401             if (!tryAgain) {
2402                 tryAgain = (retryCount > 0);
2403             }
2404
2405
2406             var notAvail = [];
2407             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2408                 var item = onAvailStack[i];
2409                 if (item) {
2410                     var el = this.getEl(item.id);
2411
2412                     if (el) {
2413                         if (!item.checkReady ||
2414                             loadComplete ||
2415                             el.nextSibling ||
2416                             (document && document.body)) {
2417
2418                             var scope = el;
2419                             if (item.override) {
2420                                 if (item.override === true) {
2421                                     scope = item.obj;
2422                                 } else {
2423                                     scope = item.override;
2424                                 }
2425                             }
2426                             item.fn.call(scope, item.obj);
2427                             onAvailStack[i] = null;
2428                         }
2429                     } else {
2430                         notAvail.push(item);
2431                     }
2432                 }
2433             }
2434
2435             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2436
2437             if (tryAgain) {
2438
2439                 this.startInterval();
2440             } else {
2441                 clearInterval(this._interval);
2442                 this._interval = null;
2443             }
2444
2445             this.locked = false;
2446
2447             return true;
2448
2449         },
2450
2451
2452         purgeElement: function(el, recurse, eventName) {
2453             var elListeners = this.getListeners(el, eventName);
2454             if (elListeners) {
2455                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2456                     var l = elListeners[i];
2457                     this.removeListener(el, l.type, l.fn);
2458                 }
2459             }
2460
2461             if (recurse && el && el.childNodes) {
2462                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2463                     this.purgeElement(el.childNodes[i], recurse, eventName);
2464                 }
2465             }
2466         },
2467
2468
2469         getListeners: function(el, eventName) {
2470             var results = [], searchLists;
2471             if (!eventName) {
2472                 searchLists = [listeners, unloadListeners];
2473             } else if (eventName == "unload") {
2474                 searchLists = [unloadListeners];
2475             } else {
2476                 searchLists = [listeners];
2477             }
2478
2479             for (var j = 0; j < searchLists.length; ++j) {
2480                 var searchList = searchLists[j];
2481                 if (searchList && searchList.length > 0) {
2482                     for (var i = 0,len = searchList.length; i < len; ++i) {
2483                         var l = searchList[i];
2484                         if (l && l[this.EL] === el &&
2485                             (!eventName || eventName === l[this.TYPE])) {
2486                             results.push({
2487                                 type:   l[this.TYPE],
2488                                 fn:     l[this.FN],
2489                                 obj:    l[this.OBJ],
2490                                 adjust: l[this.ADJ_SCOPE],
2491                                 index:  i
2492                             });
2493                         }
2494                     }
2495                 }
2496             }
2497
2498             return (results.length) ? results : null;
2499         },
2500
2501
2502         _unload: function(e) {
2503
2504             var EU = Roo.lib.Event, i, j, l, len, index;
2505
2506             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2507                 l = unloadListeners[i];
2508                 if (l) {
2509                     var scope = window;
2510                     if (l[EU.ADJ_SCOPE]) {
2511                         if (l[EU.ADJ_SCOPE] === true) {
2512                             scope = l[EU.OBJ];
2513                         } else {
2514                             scope = l[EU.ADJ_SCOPE];
2515                         }
2516                     }
2517                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2518                     unloadListeners[i] = null;
2519                     l = null;
2520                     scope = null;
2521                 }
2522             }
2523
2524             unloadListeners = null;
2525
2526             if (listeners && listeners.length > 0) {
2527                 j = listeners.length;
2528                 while (j) {
2529                     index = j - 1;
2530                     l = listeners[index];
2531                     if (l) {
2532                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2533                                 l[EU.FN], index);
2534                     }
2535                     j = j - 1;
2536                 }
2537                 l = null;
2538
2539                 EU.clearCache();
2540             }
2541
2542             EU.doRemove(window, "unload", EU._unload);
2543
2544         },
2545
2546
2547         getScroll: function() {
2548             var dd = document.documentElement, db = document.body;
2549             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2550                 return [dd.scrollTop, dd.scrollLeft];
2551             } else if (db) {
2552                 return [db.scrollTop, db.scrollLeft];
2553             } else {
2554                 return [0, 0];
2555             }
2556         },
2557
2558
2559         doAdd: function () {
2560             if (window.addEventListener) {
2561                 return function(el, eventName, fn, capture) {
2562                     el.addEventListener(eventName, fn, (capture));
2563                 };
2564             } else if (window.attachEvent) {
2565                 return function(el, eventName, fn, capture) {
2566                     el.attachEvent("on" + eventName, fn);
2567                 };
2568             } else {
2569                 return function() {
2570                 };
2571             }
2572         }(),
2573
2574
2575         doRemove: function() {
2576             if (window.removeEventListener) {
2577                 return function (el, eventName, fn, capture) {
2578                     el.removeEventListener(eventName, fn, (capture));
2579                 };
2580             } else if (window.detachEvent) {
2581                 return function (el, eventName, fn) {
2582                     el.detachEvent("on" + eventName, fn);
2583                 };
2584             } else {
2585                 return function() {
2586                 };
2587             }
2588         }()
2589     };
2590     
2591 }();
2592 (function() {     
2593    
2594     var E = Roo.lib.Event;
2595     E.on = E.addListener;
2596     E.un = E.removeListener;
2597
2598     if (document && document.body) {
2599         E._load();
2600     } else {
2601         E.doAdd(window, "load", E._load);
2602     }
2603     E.doAdd(window, "unload", E._unload);
2604     E._tryPreloadAttach();
2605 })();
2606
2607  
2608
2609 (function() {
2610     /**
2611      * @class Roo.lib.Ajax
2612      *
2613      * provide a simple Ajax request utility functions
2614      * 
2615      * Portions of this file are based on pieces of Yahoo User Interface Library
2616     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2617     * YUI licensed under the BSD License:
2618     * http://developer.yahoo.net/yui/license.txt
2619     * <script type="text/javascript">
2620     *
2621      *
2622      */
2623     Roo.lib.Ajax = {
2624         /**
2625          * @static 
2626          */
2627         request : function(method, uri, cb, data, options) {
2628             if(options){
2629                 var hs = options.headers;
2630                 if(hs){
2631                     for(var h in hs){
2632                         if(hs.hasOwnProperty(h)){
2633                             this.initHeader(h, hs[h], false);
2634                         }
2635                     }
2636                 }
2637                 if(options.xmlData){
2638                     this.initHeader('Content-Type', 'text/xml', false);
2639                     method = 'POST';
2640                     data = options.xmlData;
2641                 }
2642             }
2643
2644             return this.asyncRequest(method, uri, cb, data);
2645         },
2646         /**
2647          * serialize a form
2648          *
2649          * @static
2650          * @param {DomForm} form element
2651          * @return {String} urlencode form output.
2652          */
2653         serializeForm : function(form) {
2654             if(typeof form == 'string') {
2655                 form = (document.getElementById(form) || document.forms[form]);
2656             }
2657
2658             var el, name, val, disabled, data = '', hasSubmit = false;
2659             for (var i = 0; i < form.elements.length; i++) {
2660                 el = form.elements[i];
2661                 disabled = form.elements[i].disabled;
2662                 name = form.elements[i].name;
2663                 val = form.elements[i].value;
2664
2665                 if (!disabled && name){
2666                     switch (el.type)
2667                             {
2668                         case 'select-one':
2669                         case 'select-multiple':
2670                             for (var j = 0; j < el.options.length; j++) {
2671                                 if (el.options[j].selected) {
2672                                     if (Roo.isIE) {
2673                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2674                                     }
2675                                     else {
2676                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2677                                     }
2678                                 }
2679                             }
2680                             break;
2681                         case 'radio':
2682                         case 'checkbox':
2683                             if (el.checked) {
2684                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2685                             }
2686                             break;
2687                         case 'file':
2688
2689                         case undefined:
2690
2691                         case 'reset':
2692
2693                         case 'button':
2694
2695                             break;
2696                         case 'submit':
2697                             if(hasSubmit == false) {
2698                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2699                                 hasSubmit = true;
2700                             }
2701                             break;
2702                         default:
2703                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             break;
2705                     }
2706                 }
2707             }
2708             data = data.substr(0, data.length - 1);
2709             return data;
2710         },
2711
2712         headers:{},
2713
2714         hasHeaders:false,
2715
2716         useDefaultHeader:true,
2717
2718         defaultPostHeader:'application/x-www-form-urlencoded',
2719
2720         useDefaultXhrHeader:true,
2721
2722         defaultXhrHeader:'XMLHttpRequest',
2723
2724         hasDefaultHeaders:true,
2725
2726         defaultHeaders:{},
2727
2728         poll:{},
2729
2730         timeout:{},
2731
2732         pollInterval:50,
2733
2734         transactionId:0,
2735
2736         setProgId:function(id)
2737         {
2738             this.activeX.unshift(id);
2739         },
2740
2741         setDefaultPostHeader:function(b)
2742         {
2743             this.useDefaultHeader = b;
2744         },
2745
2746         setDefaultXhrHeader:function(b)
2747         {
2748             this.useDefaultXhrHeader = b;
2749         },
2750
2751         setPollingInterval:function(i)
2752         {
2753             if (typeof i == 'number' && isFinite(i)) {
2754                 this.pollInterval = i;
2755             }
2756         },
2757
2758         createXhrObject:function(transactionId)
2759         {
2760             var obj,http;
2761             try
2762             {
2763
2764                 http = new XMLHttpRequest();
2765
2766                 obj = { conn:http, tId:transactionId };
2767             }
2768             catch(e)
2769             {
2770                 for (var i = 0; i < this.activeX.length; ++i) {
2771                     try
2772                     {
2773
2774                         http = new ActiveXObject(this.activeX[i]);
2775
2776                         obj = { conn:http, tId:transactionId };
2777                         break;
2778                     }
2779                     catch(e) {
2780                     }
2781                 }
2782             }
2783             finally
2784             {
2785                 return obj;
2786             }
2787         },
2788
2789         getConnectionObject:function()
2790         {
2791             var o;
2792             var tId = this.transactionId;
2793
2794             try
2795             {
2796                 o = this.createXhrObject(tId);
2797                 if (o) {
2798                     this.transactionId++;
2799                 }
2800             }
2801             catch(e) {
2802             }
2803             finally
2804             {
2805                 return o;
2806             }
2807         },
2808
2809         asyncRequest:function(method, uri, callback, postData)
2810         {
2811             var o = this.getConnectionObject();
2812
2813             if (!o) {
2814                 return null;
2815             }
2816             else {
2817                 o.conn.open(method, uri, true);
2818
2819                 if (this.useDefaultXhrHeader) {
2820                     if (!this.defaultHeaders['X-Requested-With']) {
2821                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2822                     }
2823                 }
2824
2825                 if(postData && this.useDefaultHeader){
2826                     this.initHeader('Content-Type', this.defaultPostHeader);
2827                 }
2828
2829                  if (this.hasDefaultHeaders || this.hasHeaders) {
2830                     this.setHeader(o);
2831                 }
2832
2833                 this.handleReadyState(o, callback);
2834                 o.conn.send(postData || null);
2835
2836                 return o;
2837             }
2838         },
2839
2840         handleReadyState:function(o, callback)
2841         {
2842             var oConn = this;
2843
2844             if (callback && callback.timeout) {
2845                 
2846                 this.timeout[o.tId] = window.setTimeout(function() {
2847                     oConn.abort(o, callback, true);
2848                 }, callback.timeout);
2849             }
2850
2851             this.poll[o.tId] = window.setInterval(
2852                     function() {
2853                         if (o.conn && o.conn.readyState == 4) {
2854                             window.clearInterval(oConn.poll[o.tId]);
2855                             delete oConn.poll[o.tId];
2856
2857                             if(callback && callback.timeout) {
2858                                 window.clearTimeout(oConn.timeout[o.tId]);
2859                                 delete oConn.timeout[o.tId];
2860                             }
2861
2862                             oConn.handleTransactionResponse(o, callback);
2863                         }
2864                     }
2865                     , this.pollInterval);
2866         },
2867
2868         handleTransactionResponse:function(o, callback, isAbort)
2869         {
2870
2871             if (!callback) {
2872                 this.releaseObject(o);
2873                 return;
2874             }
2875
2876             var httpStatus, responseObject;
2877
2878             try
2879             {
2880                 if (o.conn.status !== undefined && o.conn.status != 0) {
2881                     httpStatus = o.conn.status;
2882                 }
2883                 else {
2884                     httpStatus = 13030;
2885                 }
2886             }
2887             catch(e) {
2888
2889
2890                 httpStatus = 13030;
2891             }
2892
2893             if (httpStatus >= 200 && httpStatus < 300) {
2894                 responseObject = this.createResponseObject(o, callback.argument);
2895                 if (callback.success) {
2896                     if (!callback.scope) {
2897                         callback.success(responseObject);
2898                     }
2899                     else {
2900
2901
2902                         callback.success.apply(callback.scope, [responseObject]);
2903                     }
2904                 }
2905             }
2906             else {
2907                 switch (httpStatus) {
2908
2909                     case 12002:
2910                     case 12029:
2911                     case 12030:
2912                     case 12031:
2913                     case 12152:
2914                     case 13030:
2915                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2916                         if (callback.failure) {
2917                             if (!callback.scope) {
2918                                 callback.failure(responseObject);
2919                             }
2920                             else {
2921                                 callback.failure.apply(callback.scope, [responseObject]);
2922                             }
2923                         }
2924                         break;
2925                     default:
2926                         responseObject = this.createResponseObject(o, callback.argument);
2927                         if (callback.failure) {
2928                             if (!callback.scope) {
2929                                 callback.failure(responseObject);
2930                             }
2931                             else {
2932                                 callback.failure.apply(callback.scope, [responseObject]);
2933                             }
2934                         }
2935                 }
2936             }
2937
2938             this.releaseObject(o);
2939             responseObject = null;
2940         },
2941
2942         createResponseObject:function(o, callbackArg)
2943         {
2944             var obj = {};
2945             var headerObj = {};
2946
2947             try
2948             {
2949                 var headerStr = o.conn.getAllResponseHeaders();
2950                 var header = headerStr.split('\n');
2951                 for (var i = 0; i < header.length; i++) {
2952                     var delimitPos = header[i].indexOf(':');
2953                     if (delimitPos != -1) {
2954                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2955                     }
2956                 }
2957             }
2958             catch(e) {
2959             }
2960
2961             obj.tId = o.tId;
2962             obj.status = o.conn.status;
2963             obj.statusText = o.conn.statusText;
2964             obj.getResponseHeader = headerObj;
2965             obj.getAllResponseHeaders = headerStr;
2966             obj.responseText = o.conn.responseText;
2967             obj.responseXML = o.conn.responseXML;
2968
2969             if (typeof callbackArg !== undefined) {
2970                 obj.argument = callbackArg;
2971             }
2972
2973             return obj;
2974         },
2975
2976         createExceptionObject:function(tId, callbackArg, isAbort)
2977         {
2978             var COMM_CODE = 0;
2979             var COMM_ERROR = 'communication failure';
2980             var ABORT_CODE = -1;
2981             var ABORT_ERROR = 'transaction aborted';
2982
2983             var obj = {};
2984
2985             obj.tId = tId;
2986             if (isAbort) {
2987                 obj.status = ABORT_CODE;
2988                 obj.statusText = ABORT_ERROR;
2989             }
2990             else {
2991                 obj.status = COMM_CODE;
2992                 obj.statusText = COMM_ERROR;
2993             }
2994
2995             if (callbackArg) {
2996                 obj.argument = callbackArg;
2997             }
2998
2999             return obj;
3000         },
3001
3002         initHeader:function(label, value, isDefault)
3003         {
3004             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3005
3006             if (headerObj[label] === undefined) {
3007                 headerObj[label] = value;
3008             }
3009             else {
3010
3011
3012                 headerObj[label] = value + "," + headerObj[label];
3013             }
3014
3015             if (isDefault) {
3016                 this.hasDefaultHeaders = true;
3017             }
3018             else {
3019                 this.hasHeaders = true;
3020             }
3021         },
3022
3023
3024         setHeader:function(o)
3025         {
3026             if (this.hasDefaultHeaders) {
3027                 for (var prop in this.defaultHeaders) {
3028                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3029                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3030                     }
3031                 }
3032             }
3033
3034             if (this.hasHeaders) {
3035                 for (var prop in this.headers) {
3036                     if (this.headers.hasOwnProperty(prop)) {
3037                         o.conn.setRequestHeader(prop, this.headers[prop]);
3038                     }
3039                 }
3040                 this.headers = {};
3041                 this.hasHeaders = false;
3042             }
3043         },
3044
3045         resetDefaultHeaders:function() {
3046             delete this.defaultHeaders;
3047             this.defaultHeaders = {};
3048             this.hasDefaultHeaders = false;
3049         },
3050
3051         abort:function(o, callback, isTimeout)
3052         {
3053             if(this.isCallInProgress(o)) {
3054                 o.conn.abort();
3055                 window.clearInterval(this.poll[o.tId]);
3056                 delete this.poll[o.tId];
3057                 if (isTimeout) {
3058                     delete this.timeout[o.tId];
3059                 }
3060
3061                 this.handleTransactionResponse(o, callback, true);
3062
3063                 return true;
3064             }
3065             else {
3066                 return false;
3067             }
3068         },
3069
3070
3071         isCallInProgress:function(o)
3072         {
3073             if (o && o.conn) {
3074                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3075             }
3076             else {
3077
3078                 return false;
3079             }
3080         },
3081
3082
3083         releaseObject:function(o)
3084         {
3085
3086             o.conn = null;
3087
3088             o = null;
3089         },
3090
3091         activeX:[
3092         'MSXML2.XMLHTTP.3.0',
3093         'MSXML2.XMLHTTP',
3094         'Microsoft.XMLHTTP'
3095         ]
3096
3097
3098     };
3099 })();/*
3100  * Portions of this file are based on pieces of Yahoo User Interface Library
3101  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3102  * YUI licensed under the BSD License:
3103  * http://developer.yahoo.net/yui/license.txt
3104  * <script type="text/javascript">
3105  *
3106  */
3107
3108 Roo.lib.Region = function(t, r, b, l) {
3109     this.top = t;
3110     this[1] = t;
3111     this.right = r;
3112     this.bottom = b;
3113     this.left = l;
3114     this[0] = l;
3115 };
3116
3117
3118 Roo.lib.Region.prototype = {
3119     contains : function(region) {
3120         return ( region.left >= this.left &&
3121                  region.right <= this.right &&
3122                  region.top >= this.top &&
3123                  region.bottom <= this.bottom    );
3124
3125     },
3126
3127     getArea : function() {
3128         return ( (this.bottom - this.top) * (this.right - this.left) );
3129     },
3130
3131     intersect : function(region) {
3132         var t = Math.max(this.top, region.top);
3133         var r = Math.min(this.right, region.right);
3134         var b = Math.min(this.bottom, region.bottom);
3135         var l = Math.max(this.left, region.left);
3136
3137         if (b >= t && r >= l) {
3138             return new Roo.lib.Region(t, r, b, l);
3139         } else {
3140             return null;
3141         }
3142     },
3143     union : function(region) {
3144         var t = Math.min(this.top, region.top);
3145         var r = Math.max(this.right, region.right);
3146         var b = Math.max(this.bottom, region.bottom);
3147         var l = Math.min(this.left, region.left);
3148
3149         return new Roo.lib.Region(t, r, b, l);
3150     },
3151
3152     adjust : function(t, l, b, r) {
3153         this.top += t;
3154         this.left += l;
3155         this.right += r;
3156         this.bottom += b;
3157         return this;
3158     }
3159 };
3160
3161 Roo.lib.Region.getRegion = function(el) {
3162     var p = Roo.lib.Dom.getXY(el);
3163
3164     var t = p[1];
3165     var r = p[0] + el.offsetWidth;
3166     var b = p[1] + el.offsetHeight;
3167     var l = p[0];
3168
3169     return new Roo.lib.Region(t, r, b, l);
3170 };
3171 /*
3172  * Portions of this file are based on pieces of Yahoo User Interface Library
3173  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3174  * YUI licensed under the BSD License:
3175  * http://developer.yahoo.net/yui/license.txt
3176  * <script type="text/javascript">
3177  *
3178  */
3179 //@@dep Roo.lib.Region
3180
3181
3182 Roo.lib.Point = function(x, y) {
3183     if (x instanceof Array) {
3184         y = x[1];
3185         x = x[0];
3186     }
3187     this.x = this.right = this.left = this[0] = x;
3188     this.y = this.top = this.bottom = this[1] = y;
3189 };
3190
3191 Roo.lib.Point.prototype = new Roo.lib.Region();
3192 /*
3193  * Portions of this file are based on pieces of Yahoo User Interface Library
3194  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3195  * YUI licensed under the BSD License:
3196  * http://developer.yahoo.net/yui/license.txt
3197  * <script type="text/javascript">
3198  *
3199  */
3200  
3201 (function() {   
3202
3203     Roo.lib.Anim = {
3204         scroll : function(el, args, duration, easing, cb, scope) {
3205             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3206         },
3207
3208         motion : function(el, args, duration, easing, cb, scope) {
3209             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3210         },
3211
3212         color : function(el, args, duration, easing, cb, scope) {
3213             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3214         },
3215
3216         run : function(el, args, duration, easing, cb, scope, type) {
3217             type = type || Roo.lib.AnimBase;
3218             if (typeof easing == "string") {
3219                 easing = Roo.lib.Easing[easing];
3220             }
3221             var anim = new type(el, args, duration, easing);
3222             anim.animateX(function() {
3223                 Roo.callback(cb, scope);
3224             });
3225             return anim;
3226         }
3227     };
3228 })();/*
3229  * Portions of this file are based on pieces of Yahoo User Interface Library
3230  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3231  * YUI licensed under the BSD License:
3232  * http://developer.yahoo.net/yui/license.txt
3233  * <script type="text/javascript">
3234  *
3235  */
3236
3237 (function() {    
3238     var libFlyweight;
3239     
3240     function fly(el) {
3241         if (!libFlyweight) {
3242             libFlyweight = new Roo.Element.Flyweight();
3243         }
3244         libFlyweight.dom = el;
3245         return libFlyweight;
3246     }
3247
3248     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3249     
3250    
3251     
3252     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3253         if (el) {
3254             this.init(el, attributes, duration, method);
3255         }
3256     };
3257
3258     Roo.lib.AnimBase.fly = fly;
3259     
3260     
3261     
3262     Roo.lib.AnimBase.prototype = {
3263
3264         toString: function() {
3265             var el = this.getEl();
3266             var id = el.id || el.tagName;
3267             return ("Anim " + id);
3268         },
3269
3270         patterns: {
3271             noNegatives:        /width|height|opacity|padding/i,
3272             offsetAttribute:  /^((width|height)|(top|left))$/,
3273             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3274             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3275         },
3276
3277
3278         doMethod: function(attr, start, end) {
3279             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3280         },
3281
3282
3283         setAttribute: function(attr, val, unit) {
3284             if (this.patterns.noNegatives.test(attr)) {
3285                 val = (val > 0) ? val : 0;
3286             }
3287
3288             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3289         },
3290
3291
3292         getAttribute: function(attr) {
3293             var el = this.getEl();
3294             var val = fly(el).getStyle(attr);
3295
3296             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3297                 return parseFloat(val);
3298             }
3299
3300             var a = this.patterns.offsetAttribute.exec(attr) || [];
3301             var pos = !!( a[3] );
3302             var box = !!( a[2] );
3303
3304
3305             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3306                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3307             } else {
3308                 val = 0;
3309             }
3310
3311             return val;
3312         },
3313
3314
3315         getDefaultUnit: function(attr) {
3316             if (this.patterns.defaultUnit.test(attr)) {
3317                 return 'px';
3318             }
3319
3320             return '';
3321         },
3322
3323         animateX : function(callback, scope) {
3324             var f = function() {
3325                 this.onComplete.removeListener(f);
3326                 if (typeof callback == "function") {
3327                     callback.call(scope || this, this);
3328                 }
3329             };
3330             this.onComplete.addListener(f, this);
3331             this.animate();
3332         },
3333
3334
3335         setRuntimeAttribute: function(attr) {
3336             var start;
3337             var end;
3338             var attributes = this.attributes;
3339
3340             this.runtimeAttributes[attr] = {};
3341
3342             var isset = function(prop) {
3343                 return (typeof prop !== 'undefined');
3344             };
3345
3346             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3347                 return false;
3348             }
3349
3350             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3351
3352
3353             if (isset(attributes[attr]['to'])) {
3354                 end = attributes[attr]['to'];
3355             } else if (isset(attributes[attr]['by'])) {
3356                 if (start.constructor == Array) {
3357                     end = [];
3358                     for (var i = 0, len = start.length; i < len; ++i) {
3359                         end[i] = start[i] + attributes[attr]['by'][i];
3360                     }
3361                 } else {
3362                     end = start + attributes[attr]['by'];
3363                 }
3364             }
3365
3366             this.runtimeAttributes[attr].start = start;
3367             this.runtimeAttributes[attr].end = end;
3368
3369
3370             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3371         },
3372
3373
3374         init: function(el, attributes, duration, method) {
3375
3376             var isAnimated = false;
3377
3378
3379             var startTime = null;
3380
3381
3382             var actualFrames = 0;
3383
3384
3385             el = Roo.getDom(el);
3386
3387
3388             this.attributes = attributes || {};
3389
3390
3391             this.duration = duration || 1;
3392
3393
3394             this.method = method || Roo.lib.Easing.easeNone;
3395
3396
3397             this.useSeconds = true;
3398
3399
3400             this.currentFrame = 0;
3401
3402
3403             this.totalFrames = Roo.lib.AnimMgr.fps;
3404
3405
3406             this.getEl = function() {
3407                 return el;
3408             };
3409
3410
3411             this.isAnimated = function() {
3412                 return isAnimated;
3413             };
3414
3415
3416             this.getStartTime = function() {
3417                 return startTime;
3418             };
3419
3420             this.runtimeAttributes = {};
3421
3422
3423             this.animate = function() {
3424                 if (this.isAnimated()) {
3425                     return false;
3426                 }
3427
3428                 this.currentFrame = 0;
3429
3430                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3431
3432                 Roo.lib.AnimMgr.registerElement(this);
3433             };
3434
3435
3436             this.stop = function(finish) {
3437                 if (finish) {
3438                     this.currentFrame = this.totalFrames;
3439                     this._onTween.fire();
3440                 }
3441                 Roo.lib.AnimMgr.stop(this);
3442             };
3443
3444             var onStart = function() {
3445                 this.onStart.fire();
3446
3447                 this.runtimeAttributes = {};
3448                 for (var attr in this.attributes) {
3449                     this.setRuntimeAttribute(attr);
3450                 }
3451
3452                 isAnimated = true;
3453                 actualFrames = 0;
3454                 startTime = new Date();
3455             };
3456
3457
3458             var onTween = function() {
3459                 var data = {
3460                     duration: new Date() - this.getStartTime(),
3461                     currentFrame: this.currentFrame
3462                 };
3463
3464                 data.toString = function() {
3465                     return (
3466                             'duration: ' + data.duration +
3467                             ', currentFrame: ' + data.currentFrame
3468                             );
3469                 };
3470
3471                 this.onTween.fire(data);
3472
3473                 var runtimeAttributes = this.runtimeAttributes;
3474
3475                 for (var attr in runtimeAttributes) {
3476                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3477                 }
3478
3479                 actualFrames += 1;
3480             };
3481
3482             var onComplete = function() {
3483                 var actual_duration = (new Date() - startTime) / 1000 ;
3484
3485                 var data = {
3486                     duration: actual_duration,
3487                     frames: actualFrames,
3488                     fps: actualFrames / actual_duration
3489                 };
3490
3491                 data.toString = function() {
3492                     return (
3493                             'duration: ' + data.duration +
3494                             ', frames: ' + data.frames +
3495                             ', fps: ' + data.fps
3496                             );
3497                 };
3498
3499                 isAnimated = false;
3500                 actualFrames = 0;
3501                 this.onComplete.fire(data);
3502             };
3503
3504
3505             this._onStart = new Roo.util.Event(this);
3506             this.onStart = new Roo.util.Event(this);
3507             this.onTween = new Roo.util.Event(this);
3508             this._onTween = new Roo.util.Event(this);
3509             this.onComplete = new Roo.util.Event(this);
3510             this._onComplete = new Roo.util.Event(this);
3511             this._onStart.addListener(onStart);
3512             this._onTween.addListener(onTween);
3513             this._onComplete.addListener(onComplete);
3514         }
3515     };
3516 })();
3517 /*
3518  * Portions of this file are based on pieces of Yahoo User Interface Library
3519  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3520  * YUI licensed under the BSD License:
3521  * http://developer.yahoo.net/yui/license.txt
3522  * <script type="text/javascript">
3523  *
3524  */
3525
3526 Roo.lib.AnimMgr = new function() {
3527
3528     var thread = null;
3529
3530
3531     var queue = [];
3532
3533
3534     var tweenCount = 0;
3535
3536
3537     this.fps = 1000;
3538
3539
3540     this.delay = 1;
3541
3542
3543     this.registerElement = function(tween) {
3544         queue[queue.length] = tween;
3545         tweenCount += 1;
3546         tween._onStart.fire();
3547         this.start();
3548     };
3549
3550
3551     this.unRegister = function(tween, index) {
3552         tween._onComplete.fire();
3553         index = index || getIndex(tween);
3554         if (index != -1) {
3555             queue.splice(index, 1);
3556         }
3557
3558         tweenCount -= 1;
3559         if (tweenCount <= 0) {
3560             this.stop();
3561         }
3562     };
3563
3564
3565     this.start = function() {
3566         if (thread === null) {
3567             thread = setInterval(this.run, this.delay);
3568         }
3569     };
3570
3571
3572     this.stop = function(tween) {
3573         if (!tween) {
3574             clearInterval(thread);
3575
3576             for (var i = 0, len = queue.length; i < len; ++i) {
3577                 if (queue[0].isAnimated()) {
3578                     this.unRegister(queue[0], 0);
3579                 }
3580             }
3581
3582             queue = [];
3583             thread = null;
3584             tweenCount = 0;
3585         }
3586         else {
3587             this.unRegister(tween);
3588         }
3589     };
3590
3591
3592     this.run = function() {
3593         for (var i = 0, len = queue.length; i < len; ++i) {
3594             var tween = queue[i];
3595             if (!tween || !tween.isAnimated()) {
3596                 continue;
3597             }
3598
3599             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3600             {
3601                 tween.currentFrame += 1;
3602
3603                 if (tween.useSeconds) {
3604                     correctFrame(tween);
3605                 }
3606                 tween._onTween.fire();
3607             }
3608             else {
3609                 Roo.lib.AnimMgr.stop(tween, i);
3610             }
3611         }
3612     };
3613
3614     var getIndex = function(anim) {
3615         for (var i = 0, len = queue.length; i < len; ++i) {
3616             if (queue[i] == anim) {
3617                 return i;
3618             }
3619         }
3620         return -1;
3621     };
3622
3623
3624     var correctFrame = function(tween) {
3625         var frames = tween.totalFrames;
3626         var frame = tween.currentFrame;
3627         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3628         var elapsed = (new Date() - tween.getStartTime());
3629         var tweak = 0;
3630
3631         if (elapsed < tween.duration * 1000) {
3632             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3633         } else {
3634             tweak = frames - (frame + 1);
3635         }
3636         if (tweak > 0 && isFinite(tweak)) {
3637             if (tween.currentFrame + tweak >= frames) {
3638                 tweak = frames - (frame + 1);
3639             }
3640
3641             tween.currentFrame += tweak;
3642         }
3643     };
3644 };
3645
3646     /*
3647  * Portions of this file are based on pieces of Yahoo User Interface Library
3648  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3649  * YUI licensed under the BSD License:
3650  * http://developer.yahoo.net/yui/license.txt
3651  * <script type="text/javascript">
3652  *
3653  */
3654 Roo.lib.Bezier = new function() {
3655
3656         this.getPosition = function(points, t) {
3657             var n = points.length;
3658             var tmp = [];
3659
3660             for (var i = 0; i < n; ++i) {
3661                 tmp[i] = [points[i][0], points[i][1]];
3662             }
3663
3664             for (var j = 1; j < n; ++j) {
3665                 for (i = 0; i < n - j; ++i) {
3666                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3667                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3668                 }
3669             }
3670
3671             return [ tmp[0][0], tmp[0][1] ];
3672
3673         };
3674     }; 
3675
3676 /**
3677  * @class Roo.lib.Color
3678  * @constructor
3679  * An abstract Color implementation. Concrete Color implementations should use
3680  * an instance of this function as their prototype, and implement the getRGB and
3681  * getHSL functions. getRGB should return an object representing the RGB
3682  * components of this Color, with the red, green, and blue components in the
3683  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3684  * return an object representing the HSL components of this Color, with the hue
3685  * component in the range [0,360), the saturation and lightness components in
3686  * the range [0,100], and the alpha component in the range [0,1].
3687  *
3688  *
3689  * Color.js
3690  *
3691  * Functions for Color handling and processing.
3692  *
3693  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3694  *
3695  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3696  * rights to this program, with the intention of it becoming part of the public
3697  * domain. Because this program is released into the public domain, it comes with
3698  * no warranty either expressed or implied, to the extent permitted by law.
3699  * 
3700  * For more free and public domain JavaScript code by the same author, visit:
3701  * http://www.safalra.com/web-design/javascript/
3702  * 
3703  */
3704 Roo.lib.Color = function() { }
3705
3706
3707 Roo.apply(Roo.lib.Color.prototype, {
3708   
3709   rgb : null,
3710   hsv : null,
3711   hsl : null,
3712   
3713   /**
3714    * getIntegerRGB
3715    * @return {Object} an object representing the RGBA components of this Color. The red,
3716    * green, and blue components are converted to integers in the range [0,255].
3717    * The alpha is a value in the range [0,1].
3718    */
3719   getIntegerRGB : function(){
3720
3721     // get the RGB components of this Color
3722     var rgb = this.getRGB();
3723
3724     // return the integer components
3725     return {
3726       'r' : Math.round(rgb.r),
3727       'g' : Math.round(rgb.g),
3728       'b' : Math.round(rgb.b),
3729       'a' : rgb.a
3730     };
3731
3732   },
3733
3734   /**
3735    * getPercentageRGB
3736    * @return {Object} an object representing the RGBA components of this Color. The red,
3737    * green, and blue components are converted to numbers in the range [0,100].
3738    * The alpha is a value in the range [0,1].
3739    */
3740   getPercentageRGB : function(){
3741
3742     // get the RGB components of this Color
3743     var rgb = this.getRGB();
3744
3745     // return the percentage components
3746     return {
3747       'r' : 100 * rgb.r / 255,
3748       'g' : 100 * rgb.g / 255,
3749       'b' : 100 * rgb.b / 255,
3750       'a' : rgb.a
3751     };
3752
3753   },
3754
3755   /**
3756    * getCSSHexadecimalRGB
3757    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3758    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3759    * are two-digit hexadecimal numbers.
3760    */
3761   getCSSHexadecimalRGB : function()
3762   {
3763
3764     // get the integer RGB components
3765     var rgb = this.getIntegerRGB();
3766
3767     // determine the hexadecimal equivalents
3768     var r16 = rgb.r.toString(16);
3769     var g16 = rgb.g.toString(16);
3770     var b16 = rgb.b.toString(16);
3771
3772     // return the CSS RGB Color value
3773     return '#'
3774         + (r16.length == 2 ? r16 : '0' + r16)
3775         + (g16.length == 2 ? g16 : '0' + g16)
3776         + (b16.length == 2 ? b16 : '0' + b16);
3777
3778   },
3779
3780   /**
3781    * getCSSIntegerRGB
3782    * @return {String} a string representing this Color as a CSS integer RGB Color
3783    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3784    * are integers in the range [0,255].
3785    */
3786   getCSSIntegerRGB : function(){
3787
3788     // get the integer RGB components
3789     var rgb = this.getIntegerRGB();
3790
3791     // return the CSS RGB Color value
3792     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3793
3794   },
3795
3796   /**
3797    * getCSSIntegerRGBA
3798    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3799    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3800    * b are integers in the range [0,255] and a is in the range [0,1].
3801    */
3802   getCSSIntegerRGBA : function(){
3803
3804     // get the integer RGB components
3805     var rgb = this.getIntegerRGB();
3806
3807     // return the CSS integer RGBA Color value
3808     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3809
3810   },
3811
3812   /**
3813    * getCSSPercentageRGB
3814    * @return {String} a string representing this Color as a CSS percentage RGB Color
3815    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3816    * b are in the range [0,100].
3817    */
3818   getCSSPercentageRGB : function(){
3819
3820     // get the percentage RGB components
3821     var rgb = this.getPercentageRGB();
3822
3823     // return the CSS RGB Color value
3824     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3825
3826   },
3827
3828   /**
3829    * getCSSPercentageRGBA
3830    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3831    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3832    * and b are in the range [0,100] and a is in the range [0,1].
3833    */
3834   getCSSPercentageRGBA : function(){
3835
3836     // get the percentage RGB components
3837     var rgb = this.getPercentageRGB();
3838
3839     // return the CSS percentage RGBA Color value
3840     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3841
3842   },
3843
3844   /**
3845    * getCSSHSL
3846    * @return {String} a string representing this Color as a CSS HSL Color value - that
3847    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3848    * s and l are in the range [0,100].
3849    */
3850   getCSSHSL : function(){
3851
3852     // get the HSL components
3853     var hsl = this.getHSL();
3854
3855     // return the CSS HSL Color value
3856     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3857
3858   },
3859
3860   /**
3861    * getCSSHSLA
3862    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3863    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3864    * s and l are in the range [0,100], and a is in the range [0,1].
3865    */
3866   getCSSHSLA : function(){
3867
3868     // get the HSL components
3869     var hsl = this.getHSL();
3870
3871     // return the CSS HSL Color value
3872     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3873
3874   },
3875
3876   /**
3877    * Sets the Color of the specified node to this Color. This functions sets
3878    * the CSS 'color' property for the node. The parameter is:
3879    * 
3880    * @param {DomElement} node - the node whose Color should be set
3881    */
3882   setNodeColor : function(node){
3883
3884     // set the Color of the node
3885     node.style.color = this.getCSSHexadecimalRGB();
3886
3887   },
3888
3889   /**
3890    * Sets the background Color of the specified node to this Color. This
3891    * functions sets the CSS 'background-color' property for the node. The
3892    * parameter is:
3893    *
3894    * @param {DomElement} node - the node whose background Color should be set
3895    */
3896   setNodeBackgroundColor : function(node){
3897
3898     // set the background Color of the node
3899     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3900
3901   },
3902   // convert between formats..
3903   toRGB: function()
3904   {
3905     var r = this.getIntegerRGB();
3906     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3907     
3908   },
3909   toHSL : function()
3910   {
3911      var hsl = this.getHSL();
3912   // return the CSS HSL Color value
3913     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3914     
3915   },
3916   
3917   toHSV : function()
3918   {
3919     var rgb = this.toRGB();
3920     var hsv = rgb.getHSV();
3921    // return the CSS HSL Color value
3922     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3923     
3924   },
3925   
3926   // modify  v = 0 ... 1 (eg. 0.5)
3927   saturate : function(v)
3928   {
3929       var rgb = this.toRGB();
3930       var hsv = rgb.getHSV();
3931       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3932       
3933   
3934   },
3935   
3936    
3937   /**
3938    * getRGB
3939    * @return {Object} the RGB and alpha components of this Color as an object with r,
3940    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3941    * the range [0,1].
3942    */
3943   getRGB: function(){
3944    
3945     // return the RGB components
3946     return {
3947       'r' : this.rgb.r,
3948       'g' : this.rgb.g,
3949       'b' : this.rgb.b,
3950       'a' : this.alpha
3951     };
3952
3953   },
3954
3955   /**
3956    * getHSV
3957    * @return {Object} the HSV and alpha components of this Color as an object with h,
3958    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3959    * [0,100], and a is in the range [0,1].
3960    */
3961   getHSV : function()
3962   {
3963     
3964     // calculate the HSV components if necessary
3965     if (this.hsv == null) {
3966       this.calculateHSV();
3967     }
3968
3969     // return the HSV components
3970     return {
3971       'h' : this.hsv.h,
3972       's' : this.hsv.s,
3973       'v' : this.hsv.v,
3974       'a' : this.alpha
3975     };
3976
3977   },
3978
3979   /**
3980    * getHSL
3981    * @return {Object} the HSL and alpha components of this Color as an object with h,
3982    * s, l, and a properties. h is in the range [0,360), s and l are in the range
3983    * [0,100], and a is in the range [0,1].
3984    */
3985   getHSL : function(){
3986     
3987      
3988     // calculate the HSV components if necessary
3989     if (this.hsl == null) { this.calculateHSL(); }
3990
3991     // return the HSL components
3992     return {
3993       'h' : this.hsl.h,
3994       's' : this.hsl.s,
3995       'l' : this.hsl.l,
3996       'a' : this.alpha
3997     };
3998
3999   }
4000   
4001
4002 });
4003
4004
4005 /**
4006  * @class Roo.lib.RGBColor
4007  * @extends Roo.lib.Color
4008  * Creates a Color specified in the RGB Color space, with an optional alpha
4009  * component. The parameters are:
4010  * @constructor
4011  * 
4012
4013  * @param {Number} r - the red component, clipped to the range [0,255]
4014  * @param {Number} g - the green component, clipped to the range [0,255]
4015  * @param {Number} b - the blue component, clipped to the range [0,255]
4016  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4017  *     optional and defaults to 1
4018  */
4019 Roo.lib.RGBColor = function (r, g, b, a){
4020
4021   // store the alpha component after clipping it if necessary
4022   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4023
4024   // store the RGB components after clipping them if necessary
4025   this.rgb =
4026       {
4027         'r' : Math.max(0, Math.min(255, r)),
4028         'g' : Math.max(0, Math.min(255, g)),
4029         'b' : Math.max(0, Math.min(255, b))
4030       };
4031
4032   // initialise the HSV and HSL components to null
4033   
4034
4035   /* 
4036    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4037    * range [0,360). The parameters are:
4038    *
4039    * maximum - the maximum of the RGB component values
4040    * range   - the range of the RGB component values
4041    */
4042    
4043
4044 }
4045 // this does an 'exteds'
4046 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4047
4048   
4049     getHue  : function(maximum, range)
4050     {
4051       var rgb = this.rgb;
4052        
4053       // check whether the range is zero
4054       if (range == 0){
4055   
4056         // set the hue to zero (any hue is acceptable as the Color is grey)
4057         var hue = 0;
4058   
4059       }else{
4060   
4061         // determine which of the components has the highest value and set the hue
4062         switch (maximum){
4063   
4064           // red has the highest value
4065           case rgb.r:
4066             var hue = (rgb.g - rgb.b) / range * 60;
4067             if (hue < 0) { hue += 360; }
4068             break;
4069   
4070           // green has the highest value
4071           case rgb.g:
4072             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4073             break;
4074   
4075           // blue has the highest value
4076           case rgb.b:
4077             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4078             break;
4079   
4080         }
4081   
4082       }
4083   
4084       // return the hue
4085       return hue;
4086   
4087     },
4088
4089   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4090    * be returned be the getHSV function.
4091    */
4092    calculateHSV : function(){
4093     var rgb = this.rgb;
4094     // get the maximum and range of the RGB component values
4095     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4096     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4097
4098     // store the HSV components
4099     this.hsv =
4100         {
4101           'h' : this.getHue(maximum, range),
4102           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4103           'v' : maximum / 2.55
4104         };
4105
4106   },
4107
4108   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4109    * be returned be the getHSL function.
4110    */
4111    calculateHSL : 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     // determine the lightness in the range [0,1]
4118     var l = maximum / 255 - range / 510;
4119
4120     // store the HSL components
4121     this.hsl =
4122         {
4123           'h' : this.getHue(maximum, range),
4124           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4125           'l' : 100 * l
4126         };
4127
4128   }
4129
4130 });
4131
4132 /**
4133  * @class Roo.lib.HSVColor
4134  * @extends Roo.lib.Color
4135  * Creates a Color specified in the HSV Color space, with an optional alpha
4136  * component. The parameters are:
4137  * @constructor
4138  *
4139  * @param {Number} h - the hue component, wrapped to the range [0,360)
4140  * @param {Number} s - the saturation component, clipped to the range [0,100]
4141  * @param {Number} v - the value component, clipped to the range [0,100]
4142  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4143  *     optional and defaults to 1
4144  */
4145 Roo.lib.HSVColor = function (h, s, v, a){
4146
4147   // store the alpha component after clipping it if necessary
4148   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4149
4150   // store the HSV components after clipping or wrapping them if necessary
4151   this.hsv =
4152       {
4153         'h' : (h % 360 + 360) % 360,
4154         's' : Math.max(0, Math.min(100, s)),
4155         'v' : Math.max(0, Math.min(100, v))
4156       };
4157
4158   // initialise the RGB and HSL components to null
4159   this.rgb = null;
4160   this.hsl = null;
4161 }
4162
4163 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4164   /* Calculates and stores the RGB components of this HSVColor so that they can
4165    * be returned be the getRGB function.
4166    */
4167   calculateRGB: function ()
4168   {
4169     var hsv = this.hsv;
4170     // check whether the saturation is zero
4171     if (hsv.s == 0){
4172
4173       // set the Color to the appropriate shade of grey
4174       var r = hsv.v;
4175       var g = hsv.v;
4176       var b = hsv.v;
4177
4178     }else{
4179
4180       // set some temporary values
4181       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4182       var p  = hsv.v * (1 - hsv.s / 100);
4183       var q  = hsv.v * (1 - hsv.s / 100 * f);
4184       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4185
4186       // set the RGB Color components to their temporary values
4187       switch (Math.floor(hsv.h / 60)){
4188         case 0: var r = hsv.v; var g = t; var b = p; break;
4189         case 1: var r = q; var g = hsv.v; var b = p; break;
4190         case 2: var r = p; var g = hsv.v; var b = t; break;
4191         case 3: var r = p; var g = q; var b = hsv.v; break;
4192         case 4: var r = t; var g = p; var b = hsv.v; break;
4193         case 5: var r = hsv.v; var g = p; var b = q; break;
4194       }
4195
4196     }
4197
4198     // store the RGB components
4199     this.rgb =
4200         {
4201           'r' : r * 2.55,
4202           'g' : g * 2.55,
4203           'b' : b * 2.55
4204         };
4205
4206   },
4207
4208   /* Calculates and stores the HSL components of this HSVColor so that they can
4209    * be returned be the getHSL function.
4210    */
4211   calculateHSL : function (){
4212
4213     var hsv = this.hsv;
4214     // determine the lightness in the range [0,100]
4215     var l = (2 - hsv.s / 100) * hsv.v / 2;
4216
4217     // store the HSL components
4218     this.hsl =
4219         {
4220           'h' : hsv.h,
4221           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4222           'l' : l
4223         };
4224
4225     // correct a division-by-zero error
4226     if (isNaN(hsl.s)) { hsl.s = 0; }
4227
4228   } 
4229  
4230
4231 });
4232  
4233
4234 /**
4235  * @class Roo.lib.HSLColor
4236  * @extends Roo.lib.Color
4237  *
4238  * @constructor
4239  * Creates a Color specified in the HSL Color space, with an optional alpha
4240  * component. The parameters are:
4241  *
4242  * @param {Number} h - the hue component, wrapped to the range [0,360)
4243  * @param {Number} s - the saturation component, clipped to the range [0,100]
4244  * @param {Number} l - the lightness component, clipped to the range [0,100]
4245  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4246  *     optional and defaults to 1
4247  */
4248
4249 Roo.lib.HSLColor = function(h, s, l, a){
4250
4251   // store the alpha component after clipping it if necessary
4252   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4253
4254   // store the HSL components after clipping or wrapping them if necessary
4255   this.hsl =
4256       {
4257         'h' : (h % 360 + 360) % 360,
4258         's' : Math.max(0, Math.min(100, s)),
4259         'l' : Math.max(0, Math.min(100, l))
4260       };
4261
4262   // initialise the RGB and HSV components to null
4263 }
4264
4265 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4266
4267   /* Calculates and stores the RGB components of this HSLColor so that they can
4268    * be returned be the getRGB function.
4269    */
4270   calculateRGB: function (){
4271
4272     // check whether the saturation is zero
4273     if (this.hsl.s == 0){
4274
4275       // store the RGB components representing the appropriate shade of grey
4276       this.rgb =
4277           {
4278             'r' : this.hsl.l * 2.55,
4279             'g' : this.hsl.l * 2.55,
4280             'b' : this.hsl.l * 2.55
4281           };
4282
4283     }else{
4284
4285       // set some temporary values
4286       var p = this.hsl.l < 50
4287             ? this.hsl.l * (1 + hsl.s / 100)
4288             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4289       var q = 2 * hsl.l - p;
4290
4291       // initialise the RGB components
4292       this.rgb =
4293           {
4294             'r' : (h + 120) / 60 % 6,
4295             'g' : h / 60,
4296             'b' : (h + 240) / 60 % 6
4297           };
4298
4299       // loop over the RGB components
4300       for (var key in this.rgb){
4301
4302         // ensure that the property is not inherited from the root object
4303         if (this.rgb.hasOwnProperty(key)){
4304
4305           // set the component to its value in the range [0,100]
4306           if (this.rgb[key] < 1){
4307             this.rgb[key] = q + (p - q) * this.rgb[key];
4308           }else if (this.rgb[key] < 3){
4309             this.rgb[key] = p;
4310           }else if (this.rgb[key] < 4){
4311             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4312           }else{
4313             this.rgb[key] = q;
4314           }
4315
4316           // set the component to its value in the range [0,255]
4317           this.rgb[key] *= 2.55;
4318
4319         }
4320
4321       }
4322
4323     }
4324
4325   },
4326
4327   /* Calculates and stores the HSV components of this HSLColor so that they can
4328    * be returned be the getHSL function.
4329    */
4330    calculateHSV : function(){
4331
4332     // set a temporary value
4333     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4334
4335     // store the HSV components
4336     this.hsv =
4337         {
4338           'h' : this.hsl.h,
4339           's' : 200 * t / (this.hsl.l + t),
4340           'v' : t + this.hsl.l
4341         };
4342
4343     // correct a division-by-zero error
4344     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4345
4346   }
4347  
4348
4349 });
4350 /*
4351  * Portions of this file are based on pieces of Yahoo User Interface Library
4352  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4353  * YUI licensed under the BSD License:
4354  * http://developer.yahoo.net/yui/license.txt
4355  * <script type="text/javascript">
4356  *
4357  */
4358 (function() {
4359
4360     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4361         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4362     };
4363
4364     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4365
4366     var fly = Roo.lib.AnimBase.fly;
4367     var Y = Roo.lib;
4368     var superclass = Y.ColorAnim.superclass;
4369     var proto = Y.ColorAnim.prototype;
4370
4371     proto.toString = function() {
4372         var el = this.getEl();
4373         var id = el.id || el.tagName;
4374         return ("ColorAnim " + id);
4375     };
4376
4377     proto.patterns.color = /color$/i;
4378     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4379     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4380     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4381     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4382
4383
4384     proto.parseColor = function(s) {
4385         if (s.length == 3) {
4386             return s;
4387         }
4388
4389         var c = this.patterns.hex.exec(s);
4390         if (c && c.length == 4) {
4391             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4392         }
4393
4394         c = this.patterns.rgb.exec(s);
4395         if (c && c.length == 4) {
4396             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4397         }
4398
4399         c = this.patterns.hex3.exec(s);
4400         if (c && c.length == 4) {
4401             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4402         }
4403
4404         return null;
4405     };
4406     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4407     proto.getAttribute = function(attr) {
4408         var el = this.getEl();
4409         if (this.patterns.color.test(attr)) {
4410             var val = fly(el).getStyle(attr);
4411
4412             if (this.patterns.transparent.test(val)) {
4413                 var parent = el.parentNode;
4414                 val = fly(parent).getStyle(attr);
4415
4416                 while (parent && this.patterns.transparent.test(val)) {
4417                     parent = parent.parentNode;
4418                     val = fly(parent).getStyle(attr);
4419                     if (parent.tagName.toUpperCase() == 'HTML') {
4420                         val = '#fff';
4421                     }
4422                 }
4423             }
4424         } else {
4425             val = superclass.getAttribute.call(this, attr);
4426         }
4427
4428         return val;
4429     };
4430     proto.getAttribute = function(attr) {
4431         var el = this.getEl();
4432         if (this.patterns.color.test(attr)) {
4433             var val = fly(el).getStyle(attr);
4434
4435             if (this.patterns.transparent.test(val)) {
4436                 var parent = el.parentNode;
4437                 val = fly(parent).getStyle(attr);
4438
4439                 while (parent && this.patterns.transparent.test(val)) {
4440                     parent = parent.parentNode;
4441                     val = fly(parent).getStyle(attr);
4442                     if (parent.tagName.toUpperCase() == 'HTML') {
4443                         val = '#fff';
4444                     }
4445                 }
4446             }
4447         } else {
4448             val = superclass.getAttribute.call(this, attr);
4449         }
4450
4451         return val;
4452     };
4453
4454     proto.doMethod = function(attr, start, end) {
4455         var val;
4456
4457         if (this.patterns.color.test(attr)) {
4458             val = [];
4459             for (var i = 0, len = start.length; i < len; ++i) {
4460                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4461             }
4462
4463             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4464         }
4465         else {
4466             val = superclass.doMethod.call(this, attr, start, end);
4467         }
4468
4469         return val;
4470     };
4471
4472     proto.setRuntimeAttribute = function(attr) {
4473         superclass.setRuntimeAttribute.call(this, attr);
4474
4475         if (this.patterns.color.test(attr)) {
4476             var attributes = this.attributes;
4477             var start = this.parseColor(this.runtimeAttributes[attr].start);
4478             var end = this.parseColor(this.runtimeAttributes[attr].end);
4479
4480             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4481                 end = this.parseColor(attributes[attr].by);
4482
4483                 for (var i = 0, len = start.length; i < len; ++i) {
4484                     end[i] = start[i] + end[i];
4485                 }
4486             }
4487
4488             this.runtimeAttributes[attr].start = start;
4489             this.runtimeAttributes[attr].end = end;
4490         }
4491     };
4492 })();
4493
4494 /*
4495  * Portions of this file are based on pieces of Yahoo User Interface Library
4496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4497  * YUI licensed under the BSD License:
4498  * http://developer.yahoo.net/yui/license.txt
4499  * <script type="text/javascript">
4500  *
4501  */
4502 Roo.lib.Easing = {
4503
4504
4505     easeNone: function (t, b, c, d) {
4506         return c * t / d + b;
4507     },
4508
4509
4510     easeIn: function (t, b, c, d) {
4511         return c * (t /= d) * t + b;
4512     },
4513
4514
4515     easeOut: function (t, b, c, d) {
4516         return -c * (t /= d) * (t - 2) + b;
4517     },
4518
4519
4520     easeBoth: function (t, b, c, d) {
4521         if ((t /= d / 2) < 1) {
4522             return c / 2 * t * t + b;
4523         }
4524
4525         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4526     },
4527
4528
4529     easeInStrong: function (t, b, c, d) {
4530         return c * (t /= d) * t * t * t + b;
4531     },
4532
4533
4534     easeOutStrong: function (t, b, c, d) {
4535         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4536     },
4537
4538
4539     easeBothStrong: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t * t * t + b;
4542         }
4543
4544         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4545     },
4546
4547
4548
4549     elasticIn: function (t, b, c, d, a, p) {
4550         if (t == 0) {
4551             return b;
4552         }
4553         if ((t /= d) == 1) {
4554             return b + c;
4555         }
4556         if (!p) {
4557             p = d * .3;
4558         }
4559
4560         if (!a || a < Math.abs(c)) {
4561             a = c;
4562             var s = p / 4;
4563         }
4564         else {
4565             var s = p / (2 * Math.PI) * Math.asin(c / a);
4566         }
4567
4568         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4569     },
4570
4571
4572     elasticOut: function (t, b, c, d, a, p) {
4573         if (t == 0) {
4574             return b;
4575         }
4576         if ((t /= d) == 1) {
4577             return b + c;
4578         }
4579         if (!p) {
4580             p = d * .3;
4581         }
4582
4583         if (!a || a < Math.abs(c)) {
4584             a = c;
4585             var s = p / 4;
4586         }
4587         else {
4588             var s = p / (2 * Math.PI) * Math.asin(c / a);
4589         }
4590
4591         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4592     },
4593
4594
4595     elasticBoth: function (t, b, c, d, a, p) {
4596         if (t == 0) {
4597             return b;
4598         }
4599
4600         if ((t /= d / 2) == 2) {
4601             return b + c;
4602         }
4603
4604         if (!p) {
4605             p = d * (.3 * 1.5);
4606         }
4607
4608         if (!a || a < Math.abs(c)) {
4609             a = c;
4610             var s = p / 4;
4611         }
4612         else {
4613             var s = p / (2 * Math.PI) * Math.asin(c / a);
4614         }
4615
4616         if (t < 1) {
4617             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4618                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4619         }
4620         return a * Math.pow(2, -10 * (t -= 1)) *
4621                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4622     },
4623
4624
4625
4626     backIn: function (t, b, c, d, s) {
4627         if (typeof s == 'undefined') {
4628             s = 1.70158;
4629         }
4630         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4631     },
4632
4633
4634     backOut: function (t, b, c, d, s) {
4635         if (typeof s == 'undefined') {
4636             s = 1.70158;
4637         }
4638         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4639     },
4640
4641
4642     backBoth: function (t, b, c, d, s) {
4643         if (typeof s == 'undefined') {
4644             s = 1.70158;
4645         }
4646
4647         if ((t /= d / 2 ) < 1) {
4648             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4649         }
4650         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4651     },
4652
4653
4654     bounceIn: function (t, b, c, d) {
4655         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4656     },
4657
4658
4659     bounceOut: function (t, b, c, d) {
4660         if ((t /= d) < (1 / 2.75)) {
4661             return c * (7.5625 * t * t) + b;
4662         } else if (t < (2 / 2.75)) {
4663             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4664         } else if (t < (2.5 / 2.75)) {
4665             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4666         }
4667         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4668     },
4669
4670
4671     bounceBoth: function (t, b, c, d) {
4672         if (t < d / 2) {
4673             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4674         }
4675         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4676     }
4677 };/*
4678  * Portions of this file are based on pieces of Yahoo User Interface Library
4679  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4680  * YUI licensed under the BSD License:
4681  * http://developer.yahoo.net/yui/license.txt
4682  * <script type="text/javascript">
4683  *
4684  */
4685     (function() {
4686         Roo.lib.Motion = function(el, attributes, duration, method) {
4687             if (el) {
4688                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4689             }
4690         };
4691
4692         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4693
4694
4695         var Y = Roo.lib;
4696         var superclass = Y.Motion.superclass;
4697         var proto = Y.Motion.prototype;
4698
4699         proto.toString = function() {
4700             var el = this.getEl();
4701             var id = el.id || el.tagName;
4702             return ("Motion " + id);
4703         };
4704
4705         proto.patterns.points = /^points$/i;
4706
4707         proto.setAttribute = function(attr, val, unit) {
4708             if (this.patterns.points.test(attr)) {
4709                 unit = unit || 'px';
4710                 superclass.setAttribute.call(this, 'left', val[0], unit);
4711                 superclass.setAttribute.call(this, 'top', val[1], unit);
4712             } else {
4713                 superclass.setAttribute.call(this, attr, val, unit);
4714             }
4715         };
4716
4717         proto.getAttribute = function(attr) {
4718             if (this.patterns.points.test(attr)) {
4719                 var val = [
4720                         superclass.getAttribute.call(this, 'left'),
4721                         superclass.getAttribute.call(this, 'top')
4722                         ];
4723             } else {
4724                 val = superclass.getAttribute.call(this, attr);
4725             }
4726
4727             return val;
4728         };
4729
4730         proto.doMethod = function(attr, start, end) {
4731             var val = null;
4732
4733             if (this.patterns.points.test(attr)) {
4734                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4735                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4736             } else {
4737                 val = superclass.doMethod.call(this, attr, start, end);
4738             }
4739             return val;
4740         };
4741
4742         proto.setRuntimeAttribute = function(attr) {
4743             if (this.patterns.points.test(attr)) {
4744                 var el = this.getEl();
4745                 var attributes = this.attributes;
4746                 var start;
4747                 var control = attributes['points']['control'] || [];
4748                 var end;
4749                 var i, len;
4750
4751                 if (control.length > 0 && !(control[0] instanceof Array)) {
4752                     control = [control];
4753                 } else {
4754                     var tmp = [];
4755                     for (i = 0,len = control.length; i < len; ++i) {
4756                         tmp[i] = control[i];
4757                     }
4758                     control = tmp;
4759                 }
4760
4761                 Roo.fly(el).position();
4762
4763                 if (isset(attributes['points']['from'])) {
4764                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4765                 }
4766                 else {
4767                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4768                 }
4769
4770                 start = this.getAttribute('points');
4771
4772
4773                 if (isset(attributes['points']['to'])) {
4774                     end = translateValues.call(this, attributes['points']['to'], start);
4775
4776                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4777                     for (i = 0,len = control.length; i < len; ++i) {
4778                         control[i] = translateValues.call(this, control[i], start);
4779                     }
4780
4781
4782                 } else if (isset(attributes['points']['by'])) {
4783                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4784
4785                     for (i = 0,len = control.length; i < len; ++i) {
4786                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4787                     }
4788                 }
4789
4790                 this.runtimeAttributes[attr] = [start];
4791
4792                 if (control.length > 0) {
4793                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4794                 }
4795
4796                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4797             }
4798             else {
4799                 superclass.setRuntimeAttribute.call(this, attr);
4800             }
4801         };
4802
4803         var translateValues = function(val, start) {
4804             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4805             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4806
4807             return val;
4808         };
4809
4810         var isset = function(prop) {
4811             return (typeof prop !== 'undefined');
4812         };
4813     })();
4814 /*
4815  * Portions of this file are based on pieces of Yahoo User Interface Library
4816  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4817  * YUI licensed under the BSD License:
4818  * http://developer.yahoo.net/yui/license.txt
4819  * <script type="text/javascript">
4820  *
4821  */
4822     (function() {
4823         Roo.lib.Scroll = function(el, attributes, duration, method) {
4824             if (el) {
4825                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4826             }
4827         };
4828
4829         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4830
4831
4832         var Y = Roo.lib;
4833         var superclass = Y.Scroll.superclass;
4834         var proto = Y.Scroll.prototype;
4835
4836         proto.toString = function() {
4837             var el = this.getEl();
4838             var id = el.id || el.tagName;
4839             return ("Scroll " + id);
4840         };
4841
4842         proto.doMethod = function(attr, start, end) {
4843             var val = null;
4844
4845             if (attr == 'scroll') {
4846                 val = [
4847                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4848                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4849                         ];
4850
4851             } else {
4852                 val = superclass.doMethod.call(this, attr, start, end);
4853             }
4854             return val;
4855         };
4856
4857         proto.getAttribute = function(attr) {
4858             var val = null;
4859             var el = this.getEl();
4860
4861             if (attr == 'scroll') {
4862                 val = [ el.scrollLeft, el.scrollTop ];
4863             } else {
4864                 val = superclass.getAttribute.call(this, attr);
4865             }
4866
4867             return val;
4868         };
4869
4870         proto.setAttribute = function(attr, val, unit) {
4871             var el = this.getEl();
4872
4873             if (attr == 'scroll') {
4874                 el.scrollLeft = val[0];
4875                 el.scrollTop = val[1];
4876             } else {
4877                 superclass.setAttribute.call(this, attr, val, unit);
4878             }
4879         };
4880     })();
4881 /*
4882  * Based on:
4883  * Ext JS Library 1.1.1
4884  * Copyright(c) 2006-2007, Ext JS, LLC.
4885  *
4886  * Originally Released Under LGPL - original licence link has changed is not relivant.
4887  *
4888  * Fork - LGPL
4889  * <script type="text/javascript">
4890  */
4891
4892
4893 // nasty IE9 hack - what a pile of crap that is..
4894
4895  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4896     Range.prototype.createContextualFragment = function (html) {
4897         var doc = window.document;
4898         var container = doc.createElement("div");
4899         container.innerHTML = html;
4900         var frag = doc.createDocumentFragment(), n;
4901         while ((n = container.firstChild)) {
4902             frag.appendChild(n);
4903         }
4904         return frag;
4905     };
4906 }
4907
4908 /**
4909  * @class Roo.DomHelper
4910  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4911  * 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>.
4912  * @static
4913  */
4914 Roo.DomHelper = function(){
4915     var tempTableEl = null;
4916     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4917     var tableRe = /^table|tbody|tr|td$/i;
4918     var xmlns = {};
4919     // build as innerHTML where available
4920     /** @ignore */
4921     var createHtml = function(o){
4922         if(typeof o == 'string'){
4923             return o;
4924         }
4925         var b = "";
4926         if(!o.tag){
4927             o.tag = "div";
4928         }
4929         b += "<" + o.tag;
4930         for(var attr in o){
4931             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4932             if(attr == "style"){
4933                 var s = o["style"];
4934                 if(typeof s == "function"){
4935                     s = s.call();
4936                 }
4937                 if(typeof s == "string"){
4938                     b += ' style="' + s + '"';
4939                 }else if(typeof s == "object"){
4940                     b += ' style="';
4941                     for(var key in s){
4942                         if(typeof s[key] != "function"){
4943                             b += key + ":" + s[key] + ";";
4944                         }
4945                     }
4946                     b += '"';
4947                 }
4948             }else{
4949                 if(attr == "cls"){
4950                     b += ' class="' + o["cls"] + '"';
4951                 }else if(attr == "htmlFor"){
4952                     b += ' for="' + o["htmlFor"] + '"';
4953                 }else{
4954                     b += " " + attr + '="' + o[attr] + '"';
4955                 }
4956             }
4957         }
4958         if(emptyTags.test(o.tag)){
4959             b += "/>";
4960         }else{
4961             b += ">";
4962             var cn = o.children || o.cn;
4963             if(cn){
4964                 //http://bugs.kde.org/show_bug.cgi?id=71506
4965                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4966                     for(var i = 0, len = cn.length; i < len; i++) {
4967                         b += createHtml(cn[i], b);
4968                     }
4969                 }else{
4970                     b += createHtml(cn, b);
4971                 }
4972             }
4973             if(o.html){
4974                 b += o.html;
4975             }
4976             b += "</" + o.tag + ">";
4977         }
4978         return b;
4979     };
4980
4981     // build as dom
4982     /** @ignore */
4983     var createDom = function(o, parentNode){
4984          
4985         // defininition craeted..
4986         var ns = false;
4987         if (o.ns && o.ns != 'html') {
4988                
4989             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4990                 xmlns[o.ns] = o.xmlns;
4991                 ns = o.xmlns;
4992             }
4993             if (typeof(xmlns[o.ns]) == 'undefined') {
4994                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4995             }
4996             ns = xmlns[o.ns];
4997         }
4998         
4999         
5000         if (typeof(o) == 'string') {
5001             return parentNode.appendChild(document.createTextNode(o));
5002         }
5003         o.tag = o.tag || div;
5004         if (o.ns && Roo.isIE) {
5005             ns = false;
5006             o.tag = o.ns + ':' + o.tag;
5007             
5008         }
5009         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5010         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5011         for(var attr in o){
5012             
5013             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5014                     attr == "style" || typeof o[attr] == "function") { continue; }
5015                     
5016             if(attr=="cls" && Roo.isIE){
5017                 el.className = o["cls"];
5018             }else{
5019                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5020                 else { 
5021                     el[attr] = o[attr];
5022                 }
5023             }
5024         }
5025         Roo.DomHelper.applyStyles(el, o.style);
5026         var cn = o.children || o.cn;
5027         if(cn){
5028             //http://bugs.kde.org/show_bug.cgi?id=71506
5029              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5030                 for(var i = 0, len = cn.length; i < len; i++) {
5031                     createDom(cn[i], el);
5032                 }
5033             }else{
5034                 createDom(cn, el);
5035             }
5036         }
5037         if(o.html){
5038             el.innerHTML = o.html;
5039         }
5040         if(parentNode){
5041            parentNode.appendChild(el);
5042         }
5043         return el;
5044     };
5045
5046     var ieTable = function(depth, s, h, e){
5047         tempTableEl.innerHTML = [s, h, e].join('');
5048         var i = -1, el = tempTableEl;
5049         while(++i < depth && el.firstChild){
5050             el = el.firstChild;
5051         }
5052         return el;
5053     };
5054
5055     // kill repeat to save bytes
5056     var ts = '<table>',
5057         te = '</table>',
5058         tbs = ts+'<tbody>',
5059         tbe = '</tbody>'+te,
5060         trs = tbs + '<tr>',
5061         tre = '</tr>'+tbe;
5062
5063     /**
5064      * @ignore
5065      * Nasty code for IE's broken table implementation
5066      */
5067     var insertIntoTable = function(tag, where, el, html){
5068         if(!tempTableEl){
5069             tempTableEl = document.createElement('div');
5070         }
5071         var node;
5072         var before = null;
5073         if(tag == 'td'){
5074             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5075                 return;
5076             }
5077             if(where == 'beforebegin'){
5078                 before = el;
5079                 el = el.parentNode;
5080             } else{
5081                 before = el.nextSibling;
5082                 el = el.parentNode;
5083             }
5084             node = ieTable(4, trs, html, tre);
5085         }
5086         else if(tag == 'tr'){
5087             if(where == 'beforebegin'){
5088                 before = el;
5089                 el = el.parentNode;
5090                 node = ieTable(3, tbs, html, tbe);
5091             } else if(where == 'afterend'){
5092                 before = el.nextSibling;
5093                 el = el.parentNode;
5094                 node = ieTable(3, tbs, html, tbe);
5095             } else{ // INTO a TR
5096                 if(where == 'afterbegin'){
5097                     before = el.firstChild;
5098                 }
5099                 node = ieTable(4, trs, html, tre);
5100             }
5101         } else if(tag == 'tbody'){
5102             if(where == 'beforebegin'){
5103                 before = el;
5104                 el = el.parentNode;
5105                 node = ieTable(2, ts, html, te);
5106             } else if(where == 'afterend'){
5107                 before = el.nextSibling;
5108                 el = el.parentNode;
5109                 node = ieTable(2, ts, html, te);
5110             } else{
5111                 if(where == 'afterbegin'){
5112                     before = el.firstChild;
5113                 }
5114                 node = ieTable(3, tbs, html, tbe);
5115             }
5116         } else{ // TABLE
5117             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5118                 return;
5119             }
5120             if(where == 'afterbegin'){
5121                 before = el.firstChild;
5122             }
5123             node = ieTable(2, ts, html, te);
5124         }
5125         el.insertBefore(node, before);
5126         return node;
5127     };
5128
5129     return {
5130     /** True to force the use of DOM instead of html fragments @type Boolean */
5131     useDom : false,
5132
5133     /**
5134      * Returns the markup for the passed Element(s) config
5135      * @param {Object} o The Dom object spec (and children)
5136      * @return {String}
5137      */
5138     markup : function(o){
5139         return createHtml(o);
5140     },
5141
5142     /**
5143      * Applies a style specification to an element
5144      * @param {String/HTMLElement} el The element to apply styles to
5145      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5146      * a function which returns such a specification.
5147      */
5148     applyStyles : function(el, styles){
5149         if(styles){
5150            el = Roo.fly(el);
5151            if(typeof styles == "string"){
5152                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5153                var matches;
5154                while ((matches = re.exec(styles)) != null){
5155                    el.setStyle(matches[1], matches[2]);
5156                }
5157            }else if (typeof styles == "object"){
5158                for (var style in styles){
5159                   el.setStyle(style, styles[style]);
5160                }
5161            }else if (typeof styles == "function"){
5162                 Roo.DomHelper.applyStyles(el, styles.call());
5163            }
5164         }
5165     },
5166
5167     /**
5168      * Inserts an HTML fragment into the Dom
5169      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5170      * @param {HTMLElement} el The context element
5171      * @param {String} html The HTML fragmenet
5172      * @return {HTMLElement} The new node
5173      */
5174     insertHtml : function(where, el, html){
5175         where = where.toLowerCase();
5176         if(el.insertAdjacentHTML){
5177             if(tableRe.test(el.tagName)){
5178                 var rs;
5179                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5180                     return rs;
5181                 }
5182             }
5183             switch(where){
5184                 case "beforebegin":
5185                     el.insertAdjacentHTML('BeforeBegin', html);
5186                     return el.previousSibling;
5187                 case "afterbegin":
5188                     el.insertAdjacentHTML('AfterBegin', html);
5189                     return el.firstChild;
5190                 case "beforeend":
5191                     el.insertAdjacentHTML('BeforeEnd', html);
5192                     return el.lastChild;
5193                 case "afterend":
5194                     el.insertAdjacentHTML('AfterEnd', html);
5195                     return el.nextSibling;
5196             }
5197             throw 'Illegal insertion point -> "' + where + '"';
5198         }
5199         var range = el.ownerDocument.createRange();
5200         var frag;
5201         switch(where){
5202              case "beforebegin":
5203                 range.setStartBefore(el);
5204                 frag = range.createContextualFragment(html);
5205                 el.parentNode.insertBefore(frag, el);
5206                 return el.previousSibling;
5207              case "afterbegin":
5208                 if(el.firstChild){
5209                     range.setStartBefore(el.firstChild);
5210                     frag = range.createContextualFragment(html);
5211                     el.insertBefore(frag, el.firstChild);
5212                     return el.firstChild;
5213                 }else{
5214                     el.innerHTML = html;
5215                     return el.firstChild;
5216                 }
5217             case "beforeend":
5218                 if(el.lastChild){
5219                     range.setStartAfter(el.lastChild);
5220                     frag = range.createContextualFragment(html);
5221                     el.appendChild(frag);
5222                     return el.lastChild;
5223                 }else{
5224                     el.innerHTML = html;
5225                     return el.lastChild;
5226                 }
5227             case "afterend":
5228                 range.setStartAfter(el);
5229                 frag = range.createContextualFragment(html);
5230                 el.parentNode.insertBefore(frag, el.nextSibling);
5231                 return el.nextSibling;
5232             }
5233             throw 'Illegal insertion point -> "' + where + '"';
5234     },
5235
5236     /**
5237      * Creates new Dom element(s) and inserts them before el
5238      * @param {String/HTMLElement/Element} el The context element
5239      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5240      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5241      * @return {HTMLElement/Roo.Element} The new node
5242      */
5243     insertBefore : function(el, o, returnElement){
5244         return this.doInsert(el, o, returnElement, "beforeBegin");
5245     },
5246
5247     /**
5248      * Creates new Dom element(s) and inserts them after el
5249      * @param {String/HTMLElement/Element} el The context element
5250      * @param {Object} o The Dom object spec (and children)
5251      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5252      * @return {HTMLElement/Roo.Element} The new node
5253      */
5254     insertAfter : function(el, o, returnElement){
5255         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5256     },
5257
5258     /**
5259      * Creates new Dom element(s) and inserts them as the first child of el
5260      * @param {String/HTMLElement/Element} el The context element
5261      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5262      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5263      * @return {HTMLElement/Roo.Element} The new node
5264      */
5265     insertFirst : function(el, o, returnElement){
5266         return this.doInsert(el, o, returnElement, "afterBegin");
5267     },
5268
5269     // private
5270     doInsert : function(el, o, returnElement, pos, sibling){
5271         el = Roo.getDom(el);
5272         var newNode;
5273         if(this.useDom || o.ns){
5274             newNode = createDom(o, null);
5275             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5276         }else{
5277             var html = createHtml(o);
5278             newNode = this.insertHtml(pos, el, html);
5279         }
5280         return returnElement ? Roo.get(newNode, true) : newNode;
5281     },
5282
5283     /**
5284      * Creates new Dom element(s) and appends them to el
5285      * @param {String/HTMLElement/Element} el The context element
5286      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5287      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5288      * @return {HTMLElement/Roo.Element} The new node
5289      */
5290     append : function(el, o, returnElement){
5291         el = Roo.getDom(el);
5292         var newNode;
5293         if(this.useDom || o.ns){
5294             newNode = createDom(o, null);
5295             el.appendChild(newNode);
5296         }else{
5297             var html = createHtml(o);
5298             newNode = this.insertHtml("beforeEnd", el, html);
5299         }
5300         return returnElement ? Roo.get(newNode, true) : newNode;
5301     },
5302
5303     /**
5304      * Creates new Dom element(s) and overwrites the contents of el with them
5305      * @param {String/HTMLElement/Element} el The context element
5306      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5307      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5308      * @return {HTMLElement/Roo.Element} The new node
5309      */
5310     overwrite : function(el, o, returnElement){
5311         el = Roo.getDom(el);
5312         if (o.ns) {
5313           
5314             while (el.childNodes.length) {
5315                 el.removeChild(el.firstChild);
5316             }
5317             createDom(o, el);
5318         } else {
5319             el.innerHTML = createHtml(o);   
5320         }
5321         
5322         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5323     },
5324
5325     /**
5326      * Creates a new Roo.DomHelper.Template from the Dom object spec
5327      * @param {Object} o The Dom object spec (and children)
5328      * @return {Roo.DomHelper.Template} The new template
5329      */
5330     createTemplate : function(o){
5331         var html = createHtml(o);
5332         return new Roo.Template(html);
5333     }
5334     };
5335 }();
5336 /*
5337  * Based on:
5338  * Ext JS Library 1.1.1
5339  * Copyright(c) 2006-2007, Ext JS, LLC.
5340  *
5341  * Originally Released Under LGPL - original licence link has changed is not relivant.
5342  *
5343  * Fork - LGPL
5344  * <script type="text/javascript">
5345  */
5346  
5347 /**
5348 * @class Roo.Template
5349 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5350 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5351 * Usage:
5352 <pre><code>
5353 var t = new Roo.Template({
5354     html :  '&lt;div name="{id}"&gt;' + 
5355         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5356         '&lt;/div&gt;',
5357     myformat: function (value, allValues) {
5358         return 'XX' + value;
5359     }
5360 });
5361 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5362 </code></pre>
5363 * For more information see this blog post with examples:
5364 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5365      - Create Elements using DOM, HTML fragments and Templates</a>. 
5366 * @constructor
5367 * @param {Object} cfg - Configuration object.
5368 */
5369 Roo.Template = function(cfg){
5370     // BC!
5371     if(cfg instanceof Array){
5372         cfg = cfg.join("");
5373     }else if(arguments.length > 1){
5374         cfg = Array.prototype.join.call(arguments, "");
5375     }
5376     
5377     
5378     if (typeof(cfg) == 'object') {
5379         Roo.apply(this,cfg)
5380     } else {
5381         // bc
5382         this.html = cfg;
5383     }
5384     if (this.url) {
5385         this.load();
5386     }
5387     
5388 };
5389 Roo.Template.prototype = {
5390     
5391     /**
5392      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5393      */
5394     onLoad : false,
5395     
5396     
5397     /**
5398      * @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..
5399      *                    it should be fixed so that template is observable...
5400      */
5401     url : false,
5402     /**
5403      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5404      */
5405     html : '',
5406     
5407     
5408     compiled : false,
5409     loaded : false,
5410     /**
5411      * Returns an HTML fragment of this template with the specified values applied.
5412      * @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'})
5413      * @return {String} The HTML fragment
5414      */
5415     
5416    
5417     
5418     applyTemplate : function(values){
5419         //Roo.log(["applyTemplate", values]);
5420         try {
5421            
5422             if(this.compiled){
5423                 return this.compiled(values);
5424             }
5425             var useF = this.disableFormats !== true;
5426             var fm = Roo.util.Format, tpl = this;
5427             var fn = function(m, name, format, args){
5428                 if(format && useF){
5429                     if(format.substr(0, 5) == "this."){
5430                         return tpl.call(format.substr(5), values[name], values);
5431                     }else{
5432                         if(args){
5433                             // quoted values are required for strings in compiled templates, 
5434                             // but for non compiled we need to strip them
5435                             // quoted reversed for jsmin
5436                             var re = /^\s*['"](.*)["']\s*$/;
5437                             args = args.split(',');
5438                             for(var i = 0, len = args.length; i < len; i++){
5439                                 args[i] = args[i].replace(re, "$1");
5440                             }
5441                             args = [values[name]].concat(args);
5442                         }else{
5443                             args = [values[name]];
5444                         }
5445                         return fm[format].apply(fm, args);
5446                     }
5447                 }else{
5448                     return values[name] !== undefined ? values[name] : "";
5449                 }
5450             };
5451             return this.html.replace(this.re, fn);
5452         } catch (e) {
5453             Roo.log(e);
5454             throw e;
5455         }
5456          
5457     },
5458     
5459     loading : false,
5460       
5461     load : function ()
5462     {
5463          
5464         if (this.loading) {
5465             return;
5466         }
5467         var _t = this;
5468         
5469         this.loading = true;
5470         this.compiled = false;
5471         
5472         var cx = new Roo.data.Connection();
5473         cx.request({
5474             url : this.url,
5475             method : 'GET',
5476             success : function (response) {
5477                 _t.loading = false;
5478                 _t.url = false;
5479                 
5480                 _t.set(response.responseText,true);
5481                 _t.loaded = true;
5482                 if (_t.onLoad) {
5483                     _t.onLoad();
5484                 }
5485              },
5486             failure : function(response) {
5487                 Roo.log("Template failed to load from " + _t.url);
5488                 _t.loading = false;
5489             }
5490         });
5491     },
5492
5493     /**
5494      * Sets the HTML used as the template and optionally compiles it.
5495      * @param {String} html
5496      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5497      * @return {Roo.Template} this
5498      */
5499     set : function(html, compile){
5500         this.html = html;
5501         this.compiled = false;
5502         if(compile){
5503             this.compile();
5504         }
5505         return this;
5506     },
5507     
5508     /**
5509      * True to disable format functions (defaults to false)
5510      * @type Boolean
5511      */
5512     disableFormats : false,
5513     
5514     /**
5515     * The regular expression used to match template variables 
5516     * @type RegExp
5517     * @property 
5518     */
5519     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5520     
5521     /**
5522      * Compiles the template into an internal function, eliminating the RegEx overhead.
5523      * @return {Roo.Template} this
5524      */
5525     compile : function(){
5526         var fm = Roo.util.Format;
5527         var useF = this.disableFormats !== true;
5528         var sep = Roo.isGecko ? "+" : ",";
5529         var fn = function(m, name, format, args){
5530             if(format && useF){
5531                 args = args ? ',' + args : "";
5532                 if(format.substr(0, 5) != "this."){
5533                     format = "fm." + format + '(';
5534                 }else{
5535                     format = 'this.call("'+ format.substr(5) + '", ';
5536                     args = ", values";
5537                 }
5538             }else{
5539                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5540             }
5541             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5542         };
5543         var body;
5544         // branched to use + in gecko and [].join() in others
5545         if(Roo.isGecko){
5546             body = "this.compiled = function(values){ return '" +
5547                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5548                     "';};";
5549         }else{
5550             body = ["this.compiled = function(values){ return ['"];
5551             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5552             body.push("'].join('');};");
5553             body = body.join('');
5554         }
5555         /**
5556          * eval:var:values
5557          * eval:var:fm
5558          */
5559         eval(body);
5560         return this;
5561     },
5562     
5563     // private function used to call members
5564     call : function(fnName, value, allValues){
5565         return this[fnName](value, allValues);
5566     },
5567     
5568     /**
5569      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5570      * @param {String/HTMLElement/Roo.Element} el The context element
5571      * @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'})
5572      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5573      * @return {HTMLElement/Roo.Element} The new node or Element
5574      */
5575     insertFirst: function(el, values, returnElement){
5576         return this.doInsert('afterBegin', el, values, returnElement);
5577     },
5578
5579     /**
5580      * Applies the supplied values to the template and inserts the new node(s) before el.
5581      * @param {String/HTMLElement/Roo.Element} el The context element
5582      * @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'})
5583      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5584      * @return {HTMLElement/Roo.Element} The new node or Element
5585      */
5586     insertBefore: function(el, values, returnElement){
5587         return this.doInsert('beforeBegin', el, values, returnElement);
5588     },
5589
5590     /**
5591      * Applies the supplied values to the template and inserts the new node(s) after el.
5592      * @param {String/HTMLElement/Roo.Element} el The context element
5593      * @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'})
5594      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5595      * @return {HTMLElement/Roo.Element} The new node or Element
5596      */
5597     insertAfter : function(el, values, returnElement){
5598         return this.doInsert('afterEnd', el, values, returnElement);
5599     },
5600     
5601     /**
5602      * Applies the supplied values to the template and appends the new node(s) to el.
5603      * @param {String/HTMLElement/Roo.Element} el The context element
5604      * @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'})
5605      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5606      * @return {HTMLElement/Roo.Element} The new node or Element
5607      */
5608     append : function(el, values, returnElement){
5609         return this.doInsert('beforeEnd', el, values, returnElement);
5610     },
5611
5612     doInsert : function(where, el, values, returnEl){
5613         el = Roo.getDom(el);
5614         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5615         return returnEl ? Roo.get(newNode, true) : newNode;
5616     },
5617
5618     /**
5619      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5620      * @param {String/HTMLElement/Roo.Element} el The context element
5621      * @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'})
5622      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5623      * @return {HTMLElement/Roo.Element} The new node or Element
5624      */
5625     overwrite : function(el, values, returnElement){
5626         el = Roo.getDom(el);
5627         el.innerHTML = this.applyTemplate(values);
5628         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5629     }
5630 };
5631 /**
5632  * Alias for {@link #applyTemplate}
5633  * @method
5634  */
5635 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5636
5637 // backwards compat
5638 Roo.DomHelper.Template = Roo.Template;
5639
5640 /**
5641  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5642  * @param {String/HTMLElement} el A DOM element or its id
5643  * @returns {Roo.Template} The created template
5644  * @static
5645  */
5646 Roo.Template.from = function(el){
5647     el = Roo.getDom(el);
5648     return new Roo.Template(el.value || el.innerHTML);
5649 };/*
5650  * Based on:
5651  * Ext JS Library 1.1.1
5652  * Copyright(c) 2006-2007, Ext JS, LLC.
5653  *
5654  * Originally Released Under LGPL - original licence link has changed is not relivant.
5655  *
5656  * Fork - LGPL
5657  * <script type="text/javascript">
5658  */
5659  
5660
5661 /*
5662  * This is code is also distributed under MIT license for use
5663  * with jQuery and prototype JavaScript libraries.
5664  */
5665 /**
5666  * @class Roo.DomQuery
5667 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).
5668 <p>
5669 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>
5670
5671 <p>
5672 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.
5673 </p>
5674 <h4>Element Selectors:</h4>
5675 <ul class="list">
5676     <li> <b>*</b> any element</li>
5677     <li> <b>E</b> an element with the tag E</li>
5678     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5679     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5680     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5681     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5682 </ul>
5683 <h4>Attribute Selectors:</h4>
5684 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5685 <ul class="list">
5686     <li> <b>E[foo]</b> has an attribute "foo"</li>
5687     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5688     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5689     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5690     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5691     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5692     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5693 </ul>
5694 <h4>Pseudo Classes:</h4>
5695 <ul class="list">
5696     <li> <b>E:first-child</b> E is the first child of its parent</li>
5697     <li> <b>E:last-child</b> E is the last child of its parent</li>
5698     <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>
5699     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5700     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5701     <li> <b>E:only-child</b> E is the only child of its parent</li>
5702     <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>
5703     <li> <b>E:first</b> the first E in the resultset</li>
5704     <li> <b>E:last</b> the last E in the resultset</li>
5705     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5706     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5707     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5708     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5709     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5710     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5711     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5712     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5713     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5714 </ul>
5715 <h4>CSS Value Selectors:</h4>
5716 <ul class="list">
5717     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5718     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5719     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5720     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5721     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5722     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5723 </ul>
5724  * @static
5725  */
5726 Roo.DomQuery = function(){
5727     var cache = {}, simpleCache = {}, valueCache = {};
5728     var nonSpace = /\S/;
5729     var trimRe = /^\s+|\s+$/g;
5730     var tplRe = /\{(\d+)\}/g;
5731     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5732     var tagTokenRe = /^(#)?([\w-\*]+)/;
5733     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5734
5735     function child(p, index){
5736         var i = 0;
5737         var n = p.firstChild;
5738         while(n){
5739             if(n.nodeType == 1){
5740                if(++i == index){
5741                    return n;
5742                }
5743             }
5744             n = n.nextSibling;
5745         }
5746         return null;
5747     };
5748
5749     function next(n){
5750         while((n = n.nextSibling) && n.nodeType != 1);
5751         return n;
5752     };
5753
5754     function prev(n){
5755         while((n = n.previousSibling) && n.nodeType != 1);
5756         return n;
5757     };
5758
5759     function children(d){
5760         var n = d.firstChild, ni = -1;
5761             while(n){
5762                 var nx = n.nextSibling;
5763                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5764                     d.removeChild(n);
5765                 }else{
5766                     n.nodeIndex = ++ni;
5767                 }
5768                 n = nx;
5769             }
5770             return this;
5771         };
5772
5773     function byClassName(c, a, v){
5774         if(!v){
5775             return c;
5776         }
5777         var r = [], ri = -1, cn;
5778         for(var i = 0, ci; ci = c[i]; i++){
5779             
5780             
5781             if((' '+
5782                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5783                  +' ').indexOf(v) != -1){
5784                 r[++ri] = ci;
5785             }
5786         }
5787         return r;
5788     };
5789
5790     function attrValue(n, attr){
5791         if(!n.tagName && typeof n.length != "undefined"){
5792             n = n[0];
5793         }
5794         if(!n){
5795             return null;
5796         }
5797         if(attr == "for"){
5798             return n.htmlFor;
5799         }
5800         if(attr == "class" || attr == "className"){
5801             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5802         }
5803         return n.getAttribute(attr) || n[attr];
5804
5805     };
5806
5807     function getNodes(ns, mode, tagName){
5808         var result = [], ri = -1, cs;
5809         if(!ns){
5810             return result;
5811         }
5812         tagName = tagName || "*";
5813         if(typeof ns.getElementsByTagName != "undefined"){
5814             ns = [ns];
5815         }
5816         if(!mode){
5817             for(var i = 0, ni; ni = ns[i]; i++){
5818                 cs = ni.getElementsByTagName(tagName);
5819                 for(var j = 0, ci; ci = cs[j]; j++){
5820                     result[++ri] = ci;
5821                 }
5822             }
5823         }else if(mode == "/" || mode == ">"){
5824             var utag = tagName.toUpperCase();
5825             for(var i = 0, ni, cn; ni = ns[i]; i++){
5826                 cn = ni.children || ni.childNodes;
5827                 for(var j = 0, cj; cj = cn[j]; j++){
5828                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5829                         result[++ri] = cj;
5830                     }
5831                 }
5832             }
5833         }else if(mode == "+"){
5834             var utag = tagName.toUpperCase();
5835             for(var i = 0, n; n = ns[i]; i++){
5836                 while((n = n.nextSibling) && n.nodeType != 1);
5837                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5838                     result[++ri] = n;
5839                 }
5840             }
5841         }else if(mode == "~"){
5842             for(var i = 0, n; n = ns[i]; i++){
5843                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5844                 if(n){
5845                     result[++ri] = n;
5846                 }
5847             }
5848         }
5849         return result;
5850     };
5851
5852     function concat(a, b){
5853         if(b.slice){
5854             return a.concat(b);
5855         }
5856         for(var i = 0, l = b.length; i < l; i++){
5857             a[a.length] = b[i];
5858         }
5859         return a;
5860     }
5861
5862     function byTag(cs, tagName){
5863         if(cs.tagName || cs == document){
5864             cs = [cs];
5865         }
5866         if(!tagName){
5867             return cs;
5868         }
5869         var r = [], ri = -1;
5870         tagName = tagName.toLowerCase();
5871         for(var i = 0, ci; ci = cs[i]; i++){
5872             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5873                 r[++ri] = ci;
5874             }
5875         }
5876         return r;
5877     };
5878
5879     function byId(cs, attr, id){
5880         if(cs.tagName || cs == document){
5881             cs = [cs];
5882         }
5883         if(!id){
5884             return cs;
5885         }
5886         var r = [], ri = -1;
5887         for(var i = 0,ci; ci = cs[i]; i++){
5888             if(ci && ci.id == id){
5889                 r[++ri] = ci;
5890                 return r;
5891             }
5892         }
5893         return r;
5894     };
5895
5896     function byAttribute(cs, attr, value, op, custom){
5897         var r = [], ri = -1, st = custom=="{";
5898         var f = Roo.DomQuery.operators[op];
5899         for(var i = 0, ci; ci = cs[i]; i++){
5900             var a;
5901             if(st){
5902                 a = Roo.DomQuery.getStyle(ci, attr);
5903             }
5904             else if(attr == "class" || attr == "className"){
5905                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5906             }else if(attr == "for"){
5907                 a = ci.htmlFor;
5908             }else if(attr == "href"){
5909                 a = ci.getAttribute("href", 2);
5910             }else{
5911                 a = ci.getAttribute(attr);
5912             }
5913             if((f && f(a, value)) || (!f && a)){
5914                 r[++ri] = ci;
5915             }
5916         }
5917         return r;
5918     };
5919
5920     function byPseudo(cs, name, value){
5921         return Roo.DomQuery.pseudos[name](cs, value);
5922     };
5923
5924     // This is for IE MSXML which does not support expandos.
5925     // IE runs the same speed using setAttribute, however FF slows way down
5926     // and Safari completely fails so they need to continue to use expandos.
5927     var isIE = window.ActiveXObject ? true : false;
5928
5929     // this eval is stop the compressor from
5930     // renaming the variable to something shorter
5931     
5932     /** eval:var:batch */
5933     var batch = 30803; 
5934
5935     var key = 30803;
5936
5937     function nodupIEXml(cs){
5938         var d = ++key;
5939         cs[0].setAttribute("_nodup", d);
5940         var r = [cs[0]];
5941         for(var i = 1, len = cs.length; i < len; i++){
5942             var c = cs[i];
5943             if(!c.getAttribute("_nodup") != d){
5944                 c.setAttribute("_nodup", d);
5945                 r[r.length] = c;
5946             }
5947         }
5948         for(var i = 0, len = cs.length; i < len; i++){
5949             cs[i].removeAttribute("_nodup");
5950         }
5951         return r;
5952     }
5953
5954     function nodup(cs){
5955         if(!cs){
5956             return [];
5957         }
5958         var len = cs.length, c, i, r = cs, cj, ri = -1;
5959         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5960             return cs;
5961         }
5962         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5963             return nodupIEXml(cs);
5964         }
5965         var d = ++key;
5966         cs[0]._nodup = d;
5967         for(i = 1; c = cs[i]; i++){
5968             if(c._nodup != d){
5969                 c._nodup = d;
5970             }else{
5971                 r = [];
5972                 for(var j = 0; j < i; j++){
5973                     r[++ri] = cs[j];
5974                 }
5975                 for(j = i+1; cj = cs[j]; j++){
5976                     if(cj._nodup != d){
5977                         cj._nodup = d;
5978                         r[++ri] = cj;
5979                     }
5980                 }
5981                 return r;
5982             }
5983         }
5984         return r;
5985     }
5986
5987     function quickDiffIEXml(c1, c2){
5988         var d = ++key;
5989         for(var i = 0, len = c1.length; i < len; i++){
5990             c1[i].setAttribute("_qdiff", d);
5991         }
5992         var r = [];
5993         for(var i = 0, len = c2.length; i < len; i++){
5994             if(c2[i].getAttribute("_qdiff") != d){
5995                 r[r.length] = c2[i];
5996             }
5997         }
5998         for(var i = 0, len = c1.length; i < len; i++){
5999            c1[i].removeAttribute("_qdiff");
6000         }
6001         return r;
6002     }
6003
6004     function quickDiff(c1, c2){
6005         var len1 = c1.length;
6006         if(!len1){
6007             return c2;
6008         }
6009         if(isIE && c1[0].selectSingleNode){
6010             return quickDiffIEXml(c1, c2);
6011         }
6012         var d = ++key;
6013         for(var i = 0; i < len1; i++){
6014             c1[i]._qdiff = d;
6015         }
6016         var r = [];
6017         for(var i = 0, len = c2.length; i < len; i++){
6018             if(c2[i]._qdiff != d){
6019                 r[r.length] = c2[i];
6020             }
6021         }
6022         return r;
6023     }
6024
6025     function quickId(ns, mode, root, id){
6026         if(ns == root){
6027            var d = root.ownerDocument || root;
6028            return d.getElementById(id);
6029         }
6030         ns = getNodes(ns, mode, "*");
6031         return byId(ns, null, id);
6032     }
6033
6034     return {
6035         getStyle : function(el, name){
6036             return Roo.fly(el).getStyle(name);
6037         },
6038         /**
6039          * Compiles a selector/xpath query into a reusable function. The returned function
6040          * takes one parameter "root" (optional), which is the context node from where the query should start.
6041          * @param {String} selector The selector/xpath query
6042          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6043          * @return {Function}
6044          */
6045         compile : function(path, type){
6046             type = type || "select";
6047             
6048             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6049             var q = path, mode, lq;
6050             var tk = Roo.DomQuery.matchers;
6051             var tklen = tk.length;
6052             var mm;
6053
6054             // accept leading mode switch
6055             var lmode = q.match(modeRe);
6056             if(lmode && lmode[1]){
6057                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6058                 q = q.replace(lmode[1], "");
6059             }
6060             // strip leading slashes
6061             while(path.substr(0, 1)=="/"){
6062                 path = path.substr(1);
6063             }
6064
6065             while(q && lq != q){
6066                 lq = q;
6067                 var tm = q.match(tagTokenRe);
6068                 if(type == "select"){
6069                     if(tm){
6070                         if(tm[1] == "#"){
6071                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6072                         }else{
6073                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6074                         }
6075                         q = q.replace(tm[0], "");
6076                     }else if(q.substr(0, 1) != '@'){
6077                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6078                     }
6079                 }else{
6080                     if(tm){
6081                         if(tm[1] == "#"){
6082                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6083                         }else{
6084                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6085                         }
6086                         q = q.replace(tm[0], "");
6087                     }
6088                 }
6089                 while(!(mm = q.match(modeRe))){
6090                     var matched = false;
6091                     for(var j = 0; j < tklen; j++){
6092                         var t = tk[j];
6093                         var m = q.match(t.re);
6094                         if(m){
6095                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6096                                                     return m[i];
6097                                                 });
6098                             q = q.replace(m[0], "");
6099                             matched = true;
6100                             break;
6101                         }
6102                     }
6103                     // prevent infinite loop on bad selector
6104                     if(!matched){
6105                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6106                     }
6107                 }
6108                 if(mm[1]){
6109                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6110                     q = q.replace(mm[1], "");
6111                 }
6112             }
6113             fn[fn.length] = "return nodup(n);\n}";
6114             
6115              /** 
6116               * list of variables that need from compression as they are used by eval.
6117              *  eval:var:batch 
6118              *  eval:var:nodup
6119              *  eval:var:byTag
6120              *  eval:var:ById
6121              *  eval:var:getNodes
6122              *  eval:var:quickId
6123              *  eval:var:mode
6124              *  eval:var:root
6125              *  eval:var:n
6126              *  eval:var:byClassName
6127              *  eval:var:byPseudo
6128              *  eval:var:byAttribute
6129              *  eval:var:attrValue
6130              * 
6131              **/ 
6132             eval(fn.join(""));
6133             return f;
6134         },
6135
6136         /**
6137          * Selects a group of elements.
6138          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6139          * @param {Node} root (optional) The start of the query (defaults to document).
6140          * @return {Array}
6141          */
6142         select : function(path, root, type){
6143             if(!root || root == document){
6144                 root = document;
6145             }
6146             if(typeof root == "string"){
6147                 root = document.getElementById(root);
6148             }
6149             var paths = path.split(",");
6150             var results = [];
6151             for(var i = 0, len = paths.length; i < len; i++){
6152                 var p = paths[i].replace(trimRe, "");
6153                 if(!cache[p]){
6154                     cache[p] = Roo.DomQuery.compile(p);
6155                     if(!cache[p]){
6156                         throw p + " is not a valid selector";
6157                     }
6158                 }
6159                 var result = cache[p](root);
6160                 if(result && result != document){
6161                     results = results.concat(result);
6162                 }
6163             }
6164             if(paths.length > 1){
6165                 return nodup(results);
6166             }
6167             return results;
6168         },
6169
6170         /**
6171          * Selects a single element.
6172          * @param {String} selector The selector/xpath query
6173          * @param {Node} root (optional) The start of the query (defaults to document).
6174          * @return {Element}
6175          */
6176         selectNode : function(path, root){
6177             return Roo.DomQuery.select(path, root)[0];
6178         },
6179
6180         /**
6181          * Selects the value of a node, optionally replacing null with the defaultValue.
6182          * @param {String} selector The selector/xpath query
6183          * @param {Node} root (optional) The start of the query (defaults to document).
6184          * @param {String} defaultValue
6185          */
6186         selectValue : function(path, root, defaultValue){
6187             path = path.replace(trimRe, "");
6188             if(!valueCache[path]){
6189                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6190             }
6191             var n = valueCache[path](root);
6192             n = n[0] ? n[0] : n;
6193             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6194             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6195         },
6196
6197         /**
6198          * Selects the value of a node, parsing integers and floats.
6199          * @param {String} selector The selector/xpath query
6200          * @param {Node} root (optional) The start of the query (defaults to document).
6201          * @param {Number} defaultValue
6202          * @return {Number}
6203          */
6204         selectNumber : function(path, root, defaultValue){
6205             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6206             return parseFloat(v);
6207         },
6208
6209         /**
6210          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6211          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6212          * @param {String} selector The simple selector to test
6213          * @return {Boolean}
6214          */
6215         is : function(el, ss){
6216             if(typeof el == "string"){
6217                 el = document.getElementById(el);
6218             }
6219             var isArray = (el instanceof Array);
6220             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6221             return isArray ? (result.length == el.length) : (result.length > 0);
6222         },
6223
6224         /**
6225          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6226          * @param {Array} el An array of elements to filter
6227          * @param {String} selector The simple selector to test
6228          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6229          * the selector instead of the ones that match
6230          * @return {Array}
6231          */
6232         filter : function(els, ss, nonMatches){
6233             ss = ss.replace(trimRe, "");
6234             if(!simpleCache[ss]){
6235                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6236             }
6237             var result = simpleCache[ss](els);
6238             return nonMatches ? quickDiff(result, els) : result;
6239         },
6240
6241         /**
6242          * Collection of matching regular expressions and code snippets.
6243          */
6244         matchers : [{
6245                 re: /^\.([\w-]+)/,
6246                 select: 'n = byClassName(n, null, " {1} ");'
6247             }, {
6248                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6249                 select: 'n = byPseudo(n, "{1}", "{2}");'
6250             },{
6251                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6252                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6253             }, {
6254                 re: /^#([\w-]+)/,
6255                 select: 'n = byId(n, null, "{1}");'
6256             },{
6257                 re: /^@([\w-]+)/,
6258                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6259             }
6260         ],
6261
6262         /**
6263          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6264          * 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;.
6265          */
6266         operators : {
6267             "=" : function(a, v){
6268                 return a == v;
6269             },
6270             "!=" : function(a, v){
6271                 return a != v;
6272             },
6273             "^=" : function(a, v){
6274                 return a && a.substr(0, v.length) == v;
6275             },
6276             "$=" : function(a, v){
6277                 return a && a.substr(a.length-v.length) == v;
6278             },
6279             "*=" : function(a, v){
6280                 return a && a.indexOf(v) !== -1;
6281             },
6282             "%=" : function(a, v){
6283                 return (a % v) == 0;
6284             },
6285             "|=" : function(a, v){
6286                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6287             },
6288             "~=" : function(a, v){
6289                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6290             }
6291         },
6292
6293         /**
6294          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6295          * and the argument (if any) supplied in the selector.
6296          */
6297         pseudos : {
6298             "first-child" : function(c){
6299                 var r = [], ri = -1, n;
6300                 for(var i = 0, ci; ci = n = c[i]; i++){
6301                     while((n = n.previousSibling) && n.nodeType != 1);
6302                     if(!n){
6303                         r[++ri] = ci;
6304                     }
6305                 }
6306                 return r;
6307             },
6308
6309             "last-child" : function(c){
6310                 var r = [], ri = -1, n;
6311                 for(var i = 0, ci; ci = n = c[i]; i++){
6312                     while((n = n.nextSibling) && n.nodeType != 1);
6313                     if(!n){
6314                         r[++ri] = ci;
6315                     }
6316                 }
6317                 return r;
6318             },
6319
6320             "nth-child" : function(c, a) {
6321                 var r = [], ri = -1;
6322                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6323                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6324                 for(var i = 0, n; n = c[i]; i++){
6325                     var pn = n.parentNode;
6326                     if (batch != pn._batch) {
6327                         var j = 0;
6328                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6329                             if(cn.nodeType == 1){
6330                                cn.nodeIndex = ++j;
6331                             }
6332                         }
6333                         pn._batch = batch;
6334                     }
6335                     if (f == 1) {
6336                         if (l == 0 || n.nodeIndex == l){
6337                             r[++ri] = n;
6338                         }
6339                     } else if ((n.nodeIndex + l) % f == 0){
6340                         r[++ri] = n;
6341                     }
6342                 }
6343
6344                 return r;
6345             },
6346
6347             "only-child" : function(c){
6348                 var r = [], ri = -1;;
6349                 for(var i = 0, ci; ci = c[i]; i++){
6350                     if(!prev(ci) && !next(ci)){
6351                         r[++ri] = ci;
6352                     }
6353                 }
6354                 return r;
6355             },
6356
6357             "empty" : function(c){
6358                 var r = [], ri = -1;
6359                 for(var i = 0, ci; ci = c[i]; i++){
6360                     var cns = ci.childNodes, j = 0, cn, empty = true;
6361                     while(cn = cns[j]){
6362                         ++j;
6363                         if(cn.nodeType == 1 || cn.nodeType == 3){
6364                             empty = false;
6365                             break;
6366                         }
6367                     }
6368                     if(empty){
6369                         r[++ri] = ci;
6370                     }
6371                 }
6372                 return r;
6373             },
6374
6375             "contains" : function(c, v){
6376                 var r = [], ri = -1;
6377                 for(var i = 0, ci; ci = c[i]; i++){
6378                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6379                         r[++ri] = ci;
6380                     }
6381                 }
6382                 return r;
6383             },
6384
6385             "nodeValue" : function(c, v){
6386                 var r = [], ri = -1;
6387                 for(var i = 0, ci; ci = c[i]; i++){
6388                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6389                         r[++ri] = ci;
6390                     }
6391                 }
6392                 return r;
6393             },
6394
6395             "checked" : function(c){
6396                 var r = [], ri = -1;
6397                 for(var i = 0, ci; ci = c[i]; i++){
6398                     if(ci.checked == true){
6399                         r[++ri] = ci;
6400                     }
6401                 }
6402                 return r;
6403             },
6404
6405             "not" : function(c, ss){
6406                 return Roo.DomQuery.filter(c, ss, true);
6407             },
6408
6409             "odd" : function(c){
6410                 return this["nth-child"](c, "odd");
6411             },
6412
6413             "even" : function(c){
6414                 return this["nth-child"](c, "even");
6415             },
6416
6417             "nth" : function(c, a){
6418                 return c[a-1] || [];
6419             },
6420
6421             "first" : function(c){
6422                 return c[0] || [];
6423             },
6424
6425             "last" : function(c){
6426                 return c[c.length-1] || [];
6427             },
6428
6429             "has" : function(c, ss){
6430                 var s = Roo.DomQuery.select;
6431                 var r = [], ri = -1;
6432                 for(var i = 0, ci; ci = c[i]; i++){
6433                     if(s(ss, ci).length > 0){
6434                         r[++ri] = ci;
6435                     }
6436                 }
6437                 return r;
6438             },
6439
6440             "next" : function(c, ss){
6441                 var is = Roo.DomQuery.is;
6442                 var r = [], ri = -1;
6443                 for(var i = 0, ci; ci = c[i]; i++){
6444                     var n = next(ci);
6445                     if(n && is(n, ss)){
6446                         r[++ri] = ci;
6447                     }
6448                 }
6449                 return r;
6450             },
6451
6452             "prev" : function(c, ss){
6453                 var is = Roo.DomQuery.is;
6454                 var r = [], ri = -1;
6455                 for(var i = 0, ci; ci = c[i]; i++){
6456                     var n = prev(ci);
6457                     if(n && is(n, ss)){
6458                         r[++ri] = ci;
6459                     }
6460                 }
6461                 return r;
6462             }
6463         }
6464     };
6465 }();
6466
6467 /**
6468  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6469  * @param {String} path The selector/xpath query
6470  * @param {Node} root (optional) The start of the query (defaults to document).
6471  * @return {Array}
6472  * @member Roo
6473  * @method query
6474  */
6475 Roo.query = Roo.DomQuery.select;
6476 /*
6477  * Based on:
6478  * Ext JS Library 1.1.1
6479  * Copyright(c) 2006-2007, Ext JS, LLC.
6480  *
6481  * Originally Released Under LGPL - original licence link has changed is not relivant.
6482  *
6483  * Fork - LGPL
6484  * <script type="text/javascript">
6485  */
6486
6487 /**
6488  * @class Roo.util.Observable
6489  * Base class that provides a common interface for publishing events. Subclasses are expected to
6490  * to have a property "events" with all the events defined.<br>
6491  * For example:
6492  * <pre><code>
6493  Employee = function(name){
6494     this.name = name;
6495     this.addEvents({
6496         "fired" : true,
6497         "quit" : true
6498     });
6499  }
6500  Roo.extend(Employee, Roo.util.Observable);
6501 </code></pre>
6502  * @param {Object} config properties to use (incuding events / listeners)
6503  */
6504
6505 Roo.util.Observable = function(cfg){
6506     
6507     cfg = cfg|| {};
6508     this.addEvents(cfg.events || {});
6509     if (cfg.events) {
6510         delete cfg.events; // make sure
6511     }
6512      
6513     Roo.apply(this, cfg);
6514     
6515     if(this.listeners){
6516         this.on(this.listeners);
6517         delete this.listeners;
6518     }
6519 };
6520 Roo.util.Observable.prototype = {
6521     /** 
6522  * @cfg {Object} listeners  list of events and functions to call for this object, 
6523  * For example :
6524  * <pre><code>
6525     listeners :  { 
6526        'click' : function(e) {
6527            ..... 
6528         } ,
6529         .... 
6530     } 
6531   </code></pre>
6532  */
6533     
6534     
6535     /**
6536      * Fires the specified event with the passed parameters (minus the event name).
6537      * @param {String} eventName
6538      * @param {Object...} args Variable number of parameters are passed to handlers
6539      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6540      */
6541     fireEvent : function(){
6542         var ce = this.events[arguments[0].toLowerCase()];
6543         if(typeof ce == "object"){
6544             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6545         }else{
6546             return true;
6547         }
6548     },
6549
6550     // private
6551     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6552
6553     /**
6554      * Appends an event handler to this component
6555      * @param {String}   eventName The type of event to listen for
6556      * @param {Function} handler The method the event invokes
6557      * @param {Object}   scope (optional) The scope in which to execute the handler
6558      * function. The handler function's "this" context.
6559      * @param {Object}   options (optional) An object containing handler configuration
6560      * properties. This may contain any of the following properties:<ul>
6561      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6562      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6563      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6564      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6565      * by the specified number of milliseconds. If the event fires again within that time, the original
6566      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6567      * </ul><br>
6568      * <p>
6569      * <b>Combining Options</b><br>
6570      * Using the options argument, it is possible to combine different types of listeners:<br>
6571      * <br>
6572      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6573                 <pre><code>
6574                 el.on('click', this.onClick, this, {
6575                         single: true,
6576                 delay: 100,
6577                 forumId: 4
6578                 });
6579                 </code></pre>
6580      * <p>
6581      * <b>Attaching multiple handlers in 1 call</b><br>
6582      * The method also allows for a single argument to be passed which is a config object containing properties
6583      * which specify multiple handlers.
6584      * <pre><code>
6585                 el.on({
6586                         'click': {
6587                         fn: this.onClick,
6588                         scope: this,
6589                         delay: 100
6590                 }, 
6591                 'mouseover': {
6592                         fn: this.onMouseOver,
6593                         scope: this
6594                 },
6595                 'mouseout': {
6596                         fn: this.onMouseOut,
6597                         scope: this
6598                 }
6599                 });
6600                 </code></pre>
6601      * <p>
6602      * Or a shorthand syntax which passes the same scope object to all handlers:
6603         <pre><code>
6604                 el.on({
6605                         'click': this.onClick,
6606                 'mouseover': this.onMouseOver,
6607                 'mouseout': this.onMouseOut,
6608                 scope: this
6609                 });
6610                 </code></pre>
6611      */
6612     addListener : function(eventName, fn, scope, o){
6613         if(typeof eventName == "object"){
6614             o = eventName;
6615             for(var e in o){
6616                 if(this.filterOptRe.test(e)){
6617                     continue;
6618                 }
6619                 if(typeof o[e] == "function"){
6620                     // shared options
6621                     this.addListener(e, o[e], o.scope,  o);
6622                 }else{
6623                     // individual options
6624                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6625                 }
6626             }
6627             return;
6628         }
6629         o = (!o || typeof o == "boolean") ? {} : o;
6630         eventName = eventName.toLowerCase();
6631         var ce = this.events[eventName] || true;
6632         if(typeof ce == "boolean"){
6633             ce = new Roo.util.Event(this, eventName);
6634             this.events[eventName] = ce;
6635         }
6636         ce.addListener(fn, scope, o);
6637     },
6638
6639     /**
6640      * Removes a listener
6641      * @param {String}   eventName     The type of event to listen for
6642      * @param {Function} handler        The handler to remove
6643      * @param {Object}   scope  (optional) The scope (this object) for the handler
6644      */
6645     removeListener : function(eventName, fn, scope){
6646         var ce = this.events[eventName.toLowerCase()];
6647         if(typeof ce == "object"){
6648             ce.removeListener(fn, scope);
6649         }
6650     },
6651
6652     /**
6653      * Removes all listeners for this object
6654      */
6655     purgeListeners : function(){
6656         for(var evt in this.events){
6657             if(typeof this.events[evt] == "object"){
6658                  this.events[evt].clearListeners();
6659             }
6660         }
6661     },
6662
6663     relayEvents : function(o, events){
6664         var createHandler = function(ename){
6665             return function(){
6666                  
6667                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6668             };
6669         };
6670         for(var i = 0, len = events.length; i < len; i++){
6671             var ename = events[i];
6672             if(!this.events[ename]){
6673                 this.events[ename] = true;
6674             };
6675             o.on(ename, createHandler(ename), this);
6676         }
6677     },
6678
6679     /**
6680      * Used to define events on this Observable
6681      * @param {Object} object The object with the events defined
6682      */
6683     addEvents : function(o){
6684         if(!this.events){
6685             this.events = {};
6686         }
6687         Roo.applyIf(this.events, o);
6688     },
6689
6690     /**
6691      * Checks to see if this object has any listeners for a specified event
6692      * @param {String} eventName The name of the event to check for
6693      * @return {Boolean} True if the event is being listened for, else false
6694      */
6695     hasListener : function(eventName){
6696         var e = this.events[eventName];
6697         return typeof e == "object" && e.listeners.length > 0;
6698     }
6699 };
6700 /**
6701  * Appends an event handler to this element (shorthand for addListener)
6702  * @param {String}   eventName     The type of event to listen for
6703  * @param {Function} handler        The method the event invokes
6704  * @param {Object}   scope (optional) The scope in which to execute the handler
6705  * function. The handler function's "this" context.
6706  * @param {Object}   options  (optional)
6707  * @method
6708  */
6709 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6710 /**
6711  * Removes a listener (shorthand for removeListener)
6712  * @param {String}   eventName     The type of event to listen for
6713  * @param {Function} handler        The handler to remove
6714  * @param {Object}   scope  (optional) The scope (this object) for the handler
6715  * @method
6716  */
6717 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6718
6719 /**
6720  * Starts capture on the specified Observable. All events will be passed
6721  * to the supplied function with the event name + standard signature of the event
6722  * <b>before</b> the event is fired. If the supplied function returns false,
6723  * the event will not fire.
6724  * @param {Observable} o The Observable to capture
6725  * @param {Function} fn The function to call
6726  * @param {Object} scope (optional) The scope (this object) for the fn
6727  * @static
6728  */
6729 Roo.util.Observable.capture = function(o, fn, scope){
6730     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6731 };
6732
6733 /**
6734  * Removes <b>all</b> added captures from the Observable.
6735  * @param {Observable} o The Observable to release
6736  * @static
6737  */
6738 Roo.util.Observable.releaseCapture = function(o){
6739     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6740 };
6741
6742 (function(){
6743
6744     var createBuffered = function(h, o, scope){
6745         var task = new Roo.util.DelayedTask();
6746         return function(){
6747             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6748         };
6749     };
6750
6751     var createSingle = function(h, e, fn, scope){
6752         return function(){
6753             e.removeListener(fn, scope);
6754             return h.apply(scope, arguments);
6755         };
6756     };
6757
6758     var createDelayed = function(h, o, scope){
6759         return function(){
6760             var args = Array.prototype.slice.call(arguments, 0);
6761             setTimeout(function(){
6762                 h.apply(scope, args);
6763             }, o.delay || 10);
6764         };
6765     };
6766
6767     Roo.util.Event = function(obj, name){
6768         this.name = name;
6769         this.obj = obj;
6770         this.listeners = [];
6771     };
6772
6773     Roo.util.Event.prototype = {
6774         addListener : function(fn, scope, options){
6775             var o = options || {};
6776             scope = scope || this.obj;
6777             if(!this.isListening(fn, scope)){
6778                 var l = {fn: fn, scope: scope, options: o};
6779                 var h = fn;
6780                 if(o.delay){
6781                     h = createDelayed(h, o, scope);
6782                 }
6783                 if(o.single){
6784                     h = createSingle(h, this, fn, scope);
6785                 }
6786                 if(o.buffer){
6787                     h = createBuffered(h, o, scope);
6788                 }
6789                 l.fireFn = h;
6790                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6791                     this.listeners.push(l);
6792                 }else{
6793                     this.listeners = this.listeners.slice(0);
6794                     this.listeners.push(l);
6795                 }
6796             }
6797         },
6798
6799         findListener : function(fn, scope){
6800             scope = scope || this.obj;
6801             var ls = this.listeners;
6802             for(var i = 0, len = ls.length; i < len; i++){
6803                 var l = ls[i];
6804                 if(l.fn == fn && l.scope == scope){
6805                     return i;
6806                 }
6807             }
6808             return -1;
6809         },
6810
6811         isListening : function(fn, scope){
6812             return this.findListener(fn, scope) != -1;
6813         },
6814
6815         removeListener : function(fn, scope){
6816             var index;
6817             if((index = this.findListener(fn, scope)) != -1){
6818                 if(!this.firing){
6819                     this.listeners.splice(index, 1);
6820                 }else{
6821                     this.listeners = this.listeners.slice(0);
6822                     this.listeners.splice(index, 1);
6823                 }
6824                 return true;
6825             }
6826             return false;
6827         },
6828
6829         clearListeners : function(){
6830             this.listeners = [];
6831         },
6832
6833         fire : function(){
6834             var ls = this.listeners, scope, len = ls.length;
6835             if(len > 0){
6836                 this.firing = true;
6837                 var args = Array.prototype.slice.call(arguments, 0);                
6838                 for(var i = 0; i < len; i++){
6839                     var l = ls[i];
6840                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6841                         this.firing = false;
6842                         return false;
6843                     }
6844                 }
6845                 this.firing = false;
6846             }
6847             return true;
6848         }
6849     };
6850 })();/*
6851  * RooJS Library 
6852  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6853  *
6854  * Licence LGPL 
6855  *
6856  */
6857  
6858 /**
6859  * @class Roo.Document
6860  * @extends Roo.util.Observable
6861  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6862  * 
6863  * @param {Object} config the methods and properties of the 'base' class for the application.
6864  * 
6865  *  Generic Page handler - implement this to start your app..
6866  * 
6867  * eg.
6868  *  MyProject = new Roo.Document({
6869         events : {
6870             'load' : true // your events..
6871         },
6872         listeners : {
6873             'ready' : function() {
6874                 // fired on Roo.onReady()
6875             }
6876         }
6877  * 
6878  */
6879 Roo.Document = function(cfg) {
6880      
6881     this.addEvents({ 
6882         'ready' : true
6883     });
6884     Roo.util.Observable.call(this,cfg);
6885     
6886     var _this = this;
6887     
6888     Roo.onReady(function() {
6889         _this.fireEvent('ready');
6890     },null,false);
6891     
6892     
6893 }
6894
6895 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6896  * Based on:
6897  * Ext JS Library 1.1.1
6898  * Copyright(c) 2006-2007, Ext JS, LLC.
6899  *
6900  * Originally Released Under LGPL - original licence link has changed is not relivant.
6901  *
6902  * Fork - LGPL
6903  * <script type="text/javascript">
6904  */
6905
6906 /**
6907  * @class Roo.EventManager
6908  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6909  * several useful events directly.
6910  * See {@link Roo.EventObject} for more details on normalized event objects.
6911  * @static
6912  */
6913 Roo.EventManager = function(){
6914     var docReadyEvent, docReadyProcId, docReadyState = false;
6915     var resizeEvent, resizeTask, textEvent, textSize;
6916     var E = Roo.lib.Event;
6917     var D = Roo.lib.Dom;
6918
6919     
6920     
6921
6922     var fireDocReady = function(){
6923         if(!docReadyState){
6924             docReadyState = true;
6925             Roo.isReady = true;
6926             if(docReadyProcId){
6927                 clearInterval(docReadyProcId);
6928             }
6929             if(Roo.isGecko || Roo.isOpera) {
6930                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6931             }
6932             if(Roo.isIE){
6933                 var defer = document.getElementById("ie-deferred-loader");
6934                 if(defer){
6935                     defer.onreadystatechange = null;
6936                     defer.parentNode.removeChild(defer);
6937                 }
6938             }
6939             if(docReadyEvent){
6940                 docReadyEvent.fire();
6941                 docReadyEvent.clearListeners();
6942             }
6943         }
6944     };
6945     
6946     var initDocReady = function(){
6947         docReadyEvent = new Roo.util.Event();
6948         if(Roo.isGecko || Roo.isOpera) {
6949             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6950         }else if(Roo.isIE){
6951             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6952             var defer = document.getElementById("ie-deferred-loader");
6953             defer.onreadystatechange = function(){
6954                 if(this.readyState == "complete"){
6955                     fireDocReady();
6956                 }
6957             };
6958         }else if(Roo.isSafari){ 
6959             docReadyProcId = setInterval(function(){
6960                 var rs = document.readyState;
6961                 if(rs == "complete") {
6962                     fireDocReady();     
6963                  }
6964             }, 10);
6965         }
6966         // no matter what, make sure it fires on load
6967         E.on(window, "load", fireDocReady);
6968     };
6969
6970     var createBuffered = function(h, o){
6971         var task = new Roo.util.DelayedTask(h);
6972         return function(e){
6973             // create new event object impl so new events don't wipe out properties
6974             e = new Roo.EventObjectImpl(e);
6975             task.delay(o.buffer, h, null, [e]);
6976         };
6977     };
6978
6979     var createSingle = function(h, el, ename, fn){
6980         return function(e){
6981             Roo.EventManager.removeListener(el, ename, fn);
6982             h(e);
6983         };
6984     };
6985
6986     var createDelayed = function(h, o){
6987         return function(e){
6988             // create new event object impl so new events don't wipe out properties
6989             e = new Roo.EventObjectImpl(e);
6990             setTimeout(function(){
6991                 h(e);
6992             }, o.delay || 10);
6993         };
6994     };
6995     var transitionEndVal = false;
6996     
6997     var transitionEnd = function()
6998     {
6999         if (transitionEndVal) {
7000             return transitionEndVal;
7001         }
7002         var el = document.createElement('div');
7003
7004         var transEndEventNames = {
7005             WebkitTransition : 'webkitTransitionEnd',
7006             MozTransition    : 'transitionend',
7007             OTransition      : 'oTransitionEnd otransitionend',
7008             transition       : 'transitionend'
7009         };
7010     
7011         for (var name in transEndEventNames) {
7012             if (el.style[name] !== undefined) {
7013                 transitionEndVal = transEndEventNames[name];
7014                 return  transitionEndVal ;
7015             }
7016         }
7017     }
7018     
7019   
7020
7021     var listen = function(element, ename, opt, fn, scope)
7022     {
7023         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7024         fn = fn || o.fn; scope = scope || o.scope;
7025         var el = Roo.getDom(element);
7026         
7027         
7028         if(!el){
7029             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7030         }
7031         
7032         if (ename == 'transitionend') {
7033             ename = transitionEnd();
7034         }
7035         var h = function(e){
7036             e = Roo.EventObject.setEvent(e);
7037             var t;
7038             if(o.delegate){
7039                 t = e.getTarget(o.delegate, el);
7040                 if(!t){
7041                     return;
7042                 }
7043             }else{
7044                 t = e.target;
7045             }
7046             if(o.stopEvent === true){
7047                 e.stopEvent();
7048             }
7049             if(o.preventDefault === true){
7050                e.preventDefault();
7051             }
7052             if(o.stopPropagation === true){
7053                 e.stopPropagation();
7054             }
7055
7056             if(o.normalized === false){
7057                 e = e.browserEvent;
7058             }
7059
7060             fn.call(scope || el, e, t, o);
7061         };
7062         if(o.delay){
7063             h = createDelayed(h, o);
7064         }
7065         if(o.single){
7066             h = createSingle(h, el, ename, fn);
7067         }
7068         if(o.buffer){
7069             h = createBuffered(h, o);
7070         }
7071         
7072         fn._handlers = fn._handlers || [];
7073         
7074         
7075         fn._handlers.push([Roo.id(el), ename, h]);
7076         
7077         
7078          
7079         E.on(el, ename, h); // this adds the actuall listener to the object..
7080         
7081         
7082         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7083             el.addEventListener("DOMMouseScroll", h, false);
7084             E.on(window, 'unload', function(){
7085                 el.removeEventListener("DOMMouseScroll", h, false);
7086             });
7087         }
7088         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7089             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7090         }
7091         return h;
7092     };
7093
7094     var stopListening = function(el, ename, fn){
7095         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7096         if(hds){
7097             for(var i = 0, len = hds.length; i < len; i++){
7098                 var h = hds[i];
7099                 if(h[0] == id && h[1] == ename){
7100                     hd = h[2];
7101                     hds.splice(i, 1);
7102                     break;
7103                 }
7104             }
7105         }
7106         E.un(el, ename, hd);
7107         el = Roo.getDom(el);
7108         if(ename == "mousewheel" && el.addEventListener){
7109             el.removeEventListener("DOMMouseScroll", hd, false);
7110         }
7111         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7112             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7113         }
7114     };
7115
7116     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7117     
7118     var pub = {
7119         
7120         
7121         /** 
7122          * Fix for doc tools
7123          * @scope Roo.EventManager
7124          */
7125         
7126         
7127         /** 
7128          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7129          * object with a Roo.EventObject
7130          * @param {Function} fn        The method the event invokes
7131          * @param {Object}   scope    An object that becomes the scope of the handler
7132          * @param {boolean}  override If true, the obj passed in becomes
7133          *                             the execution scope of the listener
7134          * @return {Function} The wrapped function
7135          * @deprecated
7136          */
7137         wrap : function(fn, scope, override){
7138             return function(e){
7139                 Roo.EventObject.setEvent(e);
7140                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7141             };
7142         },
7143         
7144         /**
7145      * Appends an event handler to an element (shorthand for addListener)
7146      * @param {String/HTMLElement}   element        The html element or id to assign the
7147      * @param {String}   eventName The type of event to listen for
7148      * @param {Function} handler The method the event invokes
7149      * @param {Object}   scope (optional) The scope in which to execute the handler
7150      * function. The handler function's "this" context.
7151      * @param {Object}   options (optional) An object containing handler configuration
7152      * properties. This may contain any of the following properties:<ul>
7153      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7154      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7155      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7156      * <li>preventDefault {Boolean} True to prevent the default action</li>
7157      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7158      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7159      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7160      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7161      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7162      * by the specified number of milliseconds. If the event fires again within that time, the original
7163      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7164      * </ul><br>
7165      * <p>
7166      * <b>Combining Options</b><br>
7167      * Using the options argument, it is possible to combine different types of listeners:<br>
7168      * <br>
7169      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7170      * Code:<pre><code>
7171 el.on('click', this.onClick, this, {
7172     single: true,
7173     delay: 100,
7174     stopEvent : true,
7175     forumId: 4
7176 });</code></pre>
7177      * <p>
7178      * <b>Attaching multiple handlers in 1 call</b><br>
7179       * The method also allows for a single argument to be passed which is a config object containing properties
7180      * which specify multiple handlers.
7181      * <p>
7182      * Code:<pre><code>
7183 el.on({
7184     'click' : {
7185         fn: this.onClick
7186         scope: this,
7187         delay: 100
7188     },
7189     'mouseover' : {
7190         fn: this.onMouseOver
7191         scope: this
7192     },
7193     'mouseout' : {
7194         fn: this.onMouseOut
7195         scope: this
7196     }
7197 });</code></pre>
7198      * <p>
7199      * Or a shorthand syntax:<br>
7200      * Code:<pre><code>
7201 el.on({
7202     'click' : this.onClick,
7203     'mouseover' : this.onMouseOver,
7204     'mouseout' : this.onMouseOut
7205     scope: this
7206 });</code></pre>
7207      */
7208         addListener : function(element, eventName, fn, scope, options){
7209             if(typeof eventName == "object"){
7210                 var o = eventName;
7211                 for(var e in o){
7212                     if(propRe.test(e)){
7213                         continue;
7214                     }
7215                     if(typeof o[e] == "function"){
7216                         // shared options
7217                         listen(element, e, o, o[e], o.scope);
7218                     }else{
7219                         // individual options
7220                         listen(element, e, o[e]);
7221                     }
7222                 }
7223                 return;
7224             }
7225             return listen(element, eventName, options, fn, scope);
7226         },
7227         
7228         /**
7229          * Removes an event handler
7230          *
7231          * @param {String/HTMLElement}   element        The id or html element to remove the 
7232          *                             event from
7233          * @param {String}   eventName     The type of event
7234          * @param {Function} fn
7235          * @return {Boolean} True if a listener was actually removed
7236          */
7237         removeListener : function(element, eventName, fn){
7238             return stopListening(element, eventName, fn);
7239         },
7240         
7241         /**
7242          * Fires when the document is ready (before onload and before images are loaded). Can be 
7243          * accessed shorthanded Roo.onReady().
7244          * @param {Function} fn        The method the event invokes
7245          * @param {Object}   scope    An  object that becomes the scope of the handler
7246          * @param {boolean}  options
7247          */
7248         onDocumentReady : function(fn, scope, options){
7249             if(docReadyState){ // if it already fired
7250                 docReadyEvent.addListener(fn, scope, options);
7251                 docReadyEvent.fire();
7252                 docReadyEvent.clearListeners();
7253                 return;
7254             }
7255             if(!docReadyEvent){
7256                 initDocReady();
7257             }
7258             docReadyEvent.addListener(fn, scope, options);
7259         },
7260         
7261         /**
7262          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7263          * @param {Function} fn        The method the event invokes
7264          * @param {Object}   scope    An object that becomes the scope of the handler
7265          * @param {boolean}  options
7266          */
7267         onWindowResize : function(fn, scope, options){
7268             if(!resizeEvent){
7269                 resizeEvent = new Roo.util.Event();
7270                 resizeTask = new Roo.util.DelayedTask(function(){
7271                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7272                 });
7273                 E.on(window, "resize", function()
7274                 {
7275                     // it seems that even chrome likes to have a slight delay here.
7276                     //if(Roo.isIE){
7277                         resizeTask.delay(50);
7278                     //}else{
7279                     //    resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7280                     //}
7281                 });
7282             }
7283             resizeEvent.addListener(fn, scope, options);
7284         },
7285
7286         /**
7287          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7288          * @param {Function} fn        The method the event invokes
7289          * @param {Object}   scope    An object that becomes the scope of the handler
7290          * @param {boolean}  options
7291          */
7292         onTextResize : function(fn, scope, options){
7293             if(!textEvent){
7294                 textEvent = new Roo.util.Event();
7295                 var textEl = new Roo.Element(document.createElement('div'));
7296                 textEl.dom.className = 'x-text-resize';
7297                 textEl.dom.innerHTML = 'X';
7298                 textEl.appendTo(document.body);
7299                 textSize = textEl.dom.offsetHeight;
7300                 setInterval(function(){
7301                     if(textEl.dom.offsetHeight != textSize){
7302                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7303                     }
7304                 }, this.textResizeInterval);
7305             }
7306             textEvent.addListener(fn, scope, options);
7307         },
7308
7309         /**
7310          * Removes the passed window resize listener.
7311          * @param {Function} fn        The method the event invokes
7312          * @param {Object}   scope    The scope of handler
7313          */
7314         removeResizeListener : function(fn, scope){
7315             if(resizeEvent){
7316                 resizeEvent.removeListener(fn, scope);
7317             }
7318         },
7319
7320         // private
7321         fireResize : function(){
7322             if(resizeEvent){
7323                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7324             }   
7325         },
7326         /**
7327          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7328          */
7329         ieDeferSrc : false,
7330         /**
7331          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7332          */
7333         textResizeInterval : 50
7334     };
7335     
7336     /**
7337      * Fix for doc tools
7338      * @scopeAlias pub=Roo.EventManager
7339      */
7340     
7341      /**
7342      * Appends an event handler to an element (shorthand for addListener)
7343      * @param {String/HTMLElement}   element        The html element or id to assign the
7344      * @param {String}   eventName The type of event to listen for
7345      * @param {Function} handler The method the event invokes
7346      * @param {Object}   scope (optional) The scope in which to execute the handler
7347      * function. The handler function's "this" context.
7348      * @param {Object}   options (optional) An object containing handler configuration
7349      * properties. This may contain any of the following properties:<ul>
7350      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7351      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7352      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7353      * <li>preventDefault {Boolean} True to prevent the default action</li>
7354      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7355      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7356      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7357      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7358      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7359      * by the specified number of milliseconds. If the event fires again within that time, the original
7360      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7361      * </ul><br>
7362      * <p>
7363      * <b>Combining Options</b><br>
7364      * Using the options argument, it is possible to combine different types of listeners:<br>
7365      * <br>
7366      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7367      * Code:<pre><code>
7368 el.on('click', this.onClick, this, {
7369     single: true,
7370     delay: 100,
7371     stopEvent : true,
7372     forumId: 4
7373 });</code></pre>
7374      * <p>
7375      * <b>Attaching multiple handlers in 1 call</b><br>
7376       * The method also allows for a single argument to be passed which is a config object containing properties
7377      * which specify multiple handlers.
7378      * <p>
7379      * Code:<pre><code>
7380 el.on({
7381     'click' : {
7382         fn: this.onClick
7383         scope: this,
7384         delay: 100
7385     },
7386     'mouseover' : {
7387         fn: this.onMouseOver
7388         scope: this
7389     },
7390     'mouseout' : {
7391         fn: this.onMouseOut
7392         scope: this
7393     }
7394 });</code></pre>
7395      * <p>
7396      * Or a shorthand syntax:<br>
7397      * Code:<pre><code>
7398 el.on({
7399     'click' : this.onClick,
7400     'mouseover' : this.onMouseOver,
7401     'mouseout' : this.onMouseOut
7402     scope: this
7403 });</code></pre>
7404      */
7405     pub.on = pub.addListener;
7406     pub.un = pub.removeListener;
7407
7408     pub.stoppedMouseDownEvent = new Roo.util.Event();
7409     return pub;
7410 }();
7411 /**
7412   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7413   * @param {Function} fn        The method the event invokes
7414   * @param {Object}   scope    An  object that becomes the scope of the handler
7415   * @param {boolean}  override If true, the obj passed in becomes
7416   *                             the execution scope of the listener
7417   * @member Roo
7418   * @method onReady
7419  */
7420 Roo.onReady = Roo.EventManager.onDocumentReady;
7421
7422 Roo.onReady(function(){
7423     var bd = Roo.get(document.body);
7424     if(!bd){ return; }
7425
7426     var cls = [
7427             Roo.isIE ? "roo-ie"
7428             : Roo.isIE11 ? "roo-ie11"
7429             : Roo.isEdge ? "roo-edge"
7430             : Roo.isGecko ? "roo-gecko"
7431             : Roo.isOpera ? "roo-opera"
7432             : Roo.isSafari ? "roo-safari" : ""];
7433
7434     if(Roo.isMac){
7435         cls.push("roo-mac");
7436     }
7437     if(Roo.isLinux){
7438         cls.push("roo-linux");
7439     }
7440     if(Roo.isIOS){
7441         cls.push("roo-ios");
7442     }
7443     if(Roo.isTouch){
7444         cls.push("roo-touch");
7445     }
7446     if(Roo.isBorderBox){
7447         cls.push('roo-border-box');
7448     }
7449     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7450         var p = bd.dom.parentNode;
7451         if(p){
7452             p.className += ' roo-strict';
7453         }
7454     }
7455     bd.addClass(cls.join(' '));
7456 });
7457
7458 /**
7459  * @class Roo.EventObject
7460  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7461  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7462  * Example:
7463  * <pre><code>
7464  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7465     e.preventDefault();
7466     var target = e.getTarget();
7467     ...
7468  }
7469  var myDiv = Roo.get("myDiv");
7470  myDiv.on("click", handleClick);
7471  //or
7472  Roo.EventManager.on("myDiv", 'click', handleClick);
7473  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7474  </code></pre>
7475  * @static
7476  */
7477 Roo.EventObject = function(){
7478     
7479     var E = Roo.lib.Event;
7480     
7481     // safari keypress events for special keys return bad keycodes
7482     var safariKeys = {
7483         63234 : 37, // left
7484         63235 : 39, // right
7485         63232 : 38, // up
7486         63233 : 40, // down
7487         63276 : 33, // page up
7488         63277 : 34, // page down
7489         63272 : 46, // delete
7490         63273 : 36, // home
7491         63275 : 35  // end
7492     };
7493
7494     // normalize button clicks
7495     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7496                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7497
7498     Roo.EventObjectImpl = function(e){
7499         if(e){
7500             this.setEvent(e.browserEvent || e);
7501         }
7502     };
7503     Roo.EventObjectImpl.prototype = {
7504         /**
7505          * Used to fix doc tools.
7506          * @scope Roo.EventObject.prototype
7507          */
7508             
7509
7510         
7511         
7512         /** The normal browser event */
7513         browserEvent : null,
7514         /** The button pressed in a mouse event */
7515         button : -1,
7516         /** True if the shift key was down during the event */
7517         shiftKey : false,
7518         /** True if the control key was down during the event */
7519         ctrlKey : false,
7520         /** True if the alt key was down during the event */
7521         altKey : false,
7522
7523         /** Key constant 
7524         * @type Number */
7525         BACKSPACE : 8,
7526         /** Key constant 
7527         * @type Number */
7528         TAB : 9,
7529         /** Key constant 
7530         * @type Number */
7531         RETURN : 13,
7532         /** Key constant 
7533         * @type Number */
7534         ENTER : 13,
7535         /** Key constant 
7536         * @type Number */
7537         SHIFT : 16,
7538         /** Key constant 
7539         * @type Number */
7540         CONTROL : 17,
7541         /** Key constant 
7542         * @type Number */
7543         ESC : 27,
7544         /** Key constant 
7545         * @type Number */
7546         SPACE : 32,
7547         /** Key constant 
7548         * @type Number */
7549         PAGEUP : 33,
7550         /** Key constant 
7551         * @type Number */
7552         PAGEDOWN : 34,
7553         /** Key constant 
7554         * @type Number */
7555         END : 35,
7556         /** Key constant 
7557         * @type Number */
7558         HOME : 36,
7559         /** Key constant 
7560         * @type Number */
7561         LEFT : 37,
7562         /** Key constant 
7563         * @type Number */
7564         UP : 38,
7565         /** Key constant 
7566         * @type Number */
7567         RIGHT : 39,
7568         /** Key constant 
7569         * @type Number */
7570         DOWN : 40,
7571         /** Key constant 
7572         * @type Number */
7573         DELETE : 46,
7574         /** Key constant 
7575         * @type Number */
7576         F5 : 116,
7577
7578            /** @private */
7579         setEvent : function(e){
7580             if(e == this || (e && e.browserEvent)){ // already wrapped
7581                 return e;
7582             }
7583             this.browserEvent = e;
7584             if(e){
7585                 // normalize buttons
7586                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7587                 if(e.type == 'click' && this.button == -1){
7588                     this.button = 0;
7589                 }
7590                 this.type = e.type;
7591                 this.shiftKey = e.shiftKey;
7592                 // mac metaKey behaves like ctrlKey
7593                 this.ctrlKey = e.ctrlKey || e.metaKey;
7594                 this.altKey = e.altKey;
7595                 // in getKey these will be normalized for the mac
7596                 this.keyCode = e.keyCode;
7597                 // keyup warnings on firefox.
7598                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7599                 // cache the target for the delayed and or buffered events
7600                 this.target = E.getTarget(e);
7601                 // same for XY
7602                 this.xy = E.getXY(e);
7603             }else{
7604                 this.button = -1;
7605                 this.shiftKey = false;
7606                 this.ctrlKey = false;
7607                 this.altKey = false;
7608                 this.keyCode = 0;
7609                 this.charCode =0;
7610                 this.target = null;
7611                 this.xy = [0, 0];
7612             }
7613             return this;
7614         },
7615
7616         /**
7617          * Stop the event (preventDefault and stopPropagation)
7618          */
7619         stopEvent : function(){
7620             if(this.browserEvent){
7621                 if(this.browserEvent.type == 'mousedown'){
7622                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7623                 }
7624                 E.stopEvent(this.browserEvent);
7625             }
7626         },
7627
7628         /**
7629          * Prevents the browsers default handling of the event.
7630          */
7631         preventDefault : function(){
7632             if(this.browserEvent){
7633                 E.preventDefault(this.browserEvent);
7634             }
7635         },
7636
7637         /** @private */
7638         isNavKeyPress : function(){
7639             var k = this.keyCode;
7640             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7641             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7642         },
7643
7644         isSpecialKey : function(){
7645             var k = this.keyCode;
7646             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7647             (k == 16) || (k == 17) ||
7648             (k >= 18 && k <= 20) ||
7649             (k >= 33 && k <= 35) ||
7650             (k >= 36 && k <= 39) ||
7651             (k >= 44 && k <= 45);
7652         },
7653         /**
7654          * Cancels bubbling of the event.
7655          */
7656         stopPropagation : function(){
7657             if(this.browserEvent){
7658                 if(this.type == 'mousedown'){
7659                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7660                 }
7661                 E.stopPropagation(this.browserEvent);
7662             }
7663         },
7664
7665         /**
7666          * Gets the key code for the event.
7667          * @return {Number}
7668          */
7669         getCharCode : function(){
7670             return this.charCode || this.keyCode;
7671         },
7672
7673         /**
7674          * Returns a normalized keyCode for the event.
7675          * @return {Number} The key code
7676          */
7677         getKey : function(){
7678             var k = this.keyCode || this.charCode;
7679             return Roo.isSafari ? (safariKeys[k] || k) : k;
7680         },
7681
7682         /**
7683          * Gets the x coordinate of the event.
7684          * @return {Number}
7685          */
7686         getPageX : function(){
7687             return this.xy[0];
7688         },
7689
7690         /**
7691          * Gets the y coordinate of the event.
7692          * @return {Number}
7693          */
7694         getPageY : function(){
7695             return this.xy[1];
7696         },
7697
7698         /**
7699          * Gets the time of the event.
7700          * @return {Number}
7701          */
7702         getTime : function(){
7703             if(this.browserEvent){
7704                 return E.getTime(this.browserEvent);
7705             }
7706             return null;
7707         },
7708
7709         /**
7710          * Gets the page coordinates of the event.
7711          * @return {Array} The xy values like [x, y]
7712          */
7713         getXY : function(){
7714             return this.xy;
7715         },
7716
7717         /**
7718          * Gets the target for the event.
7719          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7720          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7721                 search as a number or element (defaults to 10 || document.body)
7722          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7723          * @return {HTMLelement}
7724          */
7725         getTarget : function(selector, maxDepth, returnEl){
7726             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7727         },
7728         /**
7729          * Gets the related target.
7730          * @return {HTMLElement}
7731          */
7732         getRelatedTarget : function(){
7733             if(this.browserEvent){
7734                 return E.getRelatedTarget(this.browserEvent);
7735             }
7736             return null;
7737         },
7738
7739         /**
7740          * Normalizes mouse wheel delta across browsers
7741          * @return {Number} The delta
7742          */
7743         getWheelDelta : function(){
7744             var e = this.browserEvent;
7745             var delta = 0;
7746             if(e.wheelDelta){ /* IE/Opera. */
7747                 delta = e.wheelDelta/120;
7748             }else if(e.detail){ /* Mozilla case. */
7749                 delta = -e.detail/3;
7750             }
7751             return delta;
7752         },
7753
7754         /**
7755          * Returns true if the control, meta, shift or alt key was pressed during this event.
7756          * @return {Boolean}
7757          */
7758         hasModifier : function(){
7759             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7760         },
7761
7762         /**
7763          * Returns true if the target of this event equals el or is a child of el
7764          * @param {String/HTMLElement/Element} el
7765          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7766          * @return {Boolean}
7767          */
7768         within : function(el, related){
7769             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7770             return t && Roo.fly(el).contains(t);
7771         },
7772
7773         getPoint : function(){
7774             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7775         }
7776     };
7777
7778     return new Roo.EventObjectImpl();
7779 }();
7780             
7781     /*
7782  * Based on:
7783  * Ext JS Library 1.1.1
7784  * Copyright(c) 2006-2007, Ext JS, LLC.
7785  *
7786  * Originally Released Under LGPL - original licence link has changed is not relivant.
7787  *
7788  * Fork - LGPL
7789  * <script type="text/javascript">
7790  */
7791
7792  
7793 // was in Composite Element!??!?!
7794  
7795 (function(){
7796     var D = Roo.lib.Dom;
7797     var E = Roo.lib.Event;
7798     var A = Roo.lib.Anim;
7799
7800     // local style camelizing for speed
7801     var propCache = {};
7802     var camelRe = /(-[a-z])/gi;
7803     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7804     var view = document.defaultView;
7805
7806 /**
7807  * @class Roo.Element
7808  * Represents an Element in the DOM.<br><br>
7809  * Usage:<br>
7810 <pre><code>
7811 var el = Roo.get("my-div");
7812
7813 // or with getEl
7814 var el = getEl("my-div");
7815
7816 // or with a DOM element
7817 var el = Roo.get(myDivElement);
7818 </code></pre>
7819  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7820  * each call instead of constructing a new one.<br><br>
7821  * <b>Animations</b><br />
7822  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7823  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7824 <pre>
7825 Option    Default   Description
7826 --------- --------  ---------------------------------------------
7827 duration  .35       The duration of the animation in seconds
7828 easing    easeOut   The YUI easing method
7829 callback  none      A function to execute when the anim completes
7830 scope     this      The scope (this) of the callback function
7831 </pre>
7832 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7833 * manipulate the animation. Here's an example:
7834 <pre><code>
7835 var el = Roo.get("my-div");
7836
7837 // no animation
7838 el.setWidth(100);
7839
7840 // default animation
7841 el.setWidth(100, true);
7842
7843 // animation with some options set
7844 el.setWidth(100, {
7845     duration: 1,
7846     callback: this.foo,
7847     scope: this
7848 });
7849
7850 // using the "anim" property to get the Anim object
7851 var opt = {
7852     duration: 1,
7853     callback: this.foo,
7854     scope: this
7855 };
7856 el.setWidth(100, opt);
7857 ...
7858 if(opt.anim.isAnimated()){
7859     opt.anim.stop();
7860 }
7861 </code></pre>
7862 * <b> Composite (Collections of) Elements</b><br />
7863  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7864  * @constructor Create a new Element directly.
7865  * @param {String/HTMLElement} element
7866  * @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).
7867  */
7868     Roo.Element = function(element, forceNew)
7869     {
7870         var dom = typeof element == "string" ?
7871                 document.getElementById(element) : element;
7872         
7873         this.listeners = {};
7874         
7875         if(!dom){ // invalid id/element
7876             return null;
7877         }
7878         var id = dom.id;
7879         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7880             return Roo.Element.cache[id];
7881         }
7882
7883         /**
7884          * The DOM element
7885          * @type HTMLElement
7886          */
7887         this.dom = dom;
7888
7889         /**
7890          * The DOM element ID
7891          * @type String
7892          */
7893         this.id = id || Roo.id(dom);
7894         
7895         return this; // assumed for cctor?
7896     };
7897
7898     var El = Roo.Element;
7899
7900     El.prototype = {
7901         /**
7902          * The element's default display mode  (defaults to "") 
7903          * @type String
7904          */
7905         originalDisplay : "",
7906
7907         
7908         // note this is overridden in BS version..
7909         visibilityMode : 1, 
7910         /**
7911          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7912          * @type String
7913          */
7914         defaultUnit : "px",
7915         
7916         /**
7917          * Sets the element's visibility mode. When setVisible() is called it
7918          * will use this to determine whether to set the visibility or the display property.
7919          * @param visMode Element.VISIBILITY or Element.DISPLAY
7920          * @return {Roo.Element} this
7921          */
7922         setVisibilityMode : function(visMode){
7923             this.visibilityMode = visMode;
7924             return this;
7925         },
7926         /**
7927          * Convenience method for setVisibilityMode(Element.DISPLAY)
7928          * @param {String} display (optional) What to set display to when visible
7929          * @return {Roo.Element} this
7930          */
7931         enableDisplayMode : function(display){
7932             this.setVisibilityMode(El.DISPLAY);
7933             if(typeof display != "undefined") { this.originalDisplay = display; }
7934             return this;
7935         },
7936
7937         /**
7938          * 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)
7939          * @param {String} selector The simple selector to test
7940          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7941                 search as a number or element (defaults to 10 || document.body)
7942          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7943          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7944          */
7945         findParent : function(simpleSelector, maxDepth, returnEl){
7946             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7947             maxDepth = maxDepth || 50;
7948             if(typeof maxDepth != "number"){
7949                 stopEl = Roo.getDom(maxDepth);
7950                 maxDepth = 10;
7951             }
7952             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7953                 if(dq.is(p, simpleSelector)){
7954                     return returnEl ? Roo.get(p) : p;
7955                 }
7956                 depth++;
7957                 p = p.parentNode;
7958             }
7959             return null;
7960         },
7961
7962
7963         /**
7964          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7965          * @param {String} selector The simple selector to test
7966          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7967                 search as a number or element (defaults to 10 || document.body)
7968          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7969          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7970          */
7971         findParentNode : function(simpleSelector, maxDepth, returnEl){
7972             var p = Roo.fly(this.dom.parentNode, '_internal');
7973             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7974         },
7975         
7976         /**
7977          * Looks at  the scrollable parent element
7978          */
7979         findScrollableParent : function()
7980         {
7981             var overflowRegex = /(auto|scroll)/;
7982             
7983             if(this.getStyle('position') === 'fixed'){
7984                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7985             }
7986             
7987             var excludeStaticParent = this.getStyle('position') === "absolute";
7988             
7989             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7990                 
7991                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7992                     continue;
7993                 }
7994                 
7995                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7996                     return parent;
7997                 }
7998                 
7999                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8000                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8001                 }
8002             }
8003             
8004             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8005         },
8006
8007         /**
8008          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8009          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8010          * @param {String} selector The simple selector to test
8011          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8012                 search as a number or element (defaults to 10 || document.body)
8013          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8014          */
8015         up : function(simpleSelector, maxDepth){
8016             return this.findParentNode(simpleSelector, maxDepth, true);
8017         },
8018
8019
8020
8021         /**
8022          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8023          * @param {String} selector The simple selector to test
8024          * @return {Boolean} True if this element matches the selector, else false
8025          */
8026         is : function(simpleSelector){
8027             return Roo.DomQuery.is(this.dom, simpleSelector);
8028         },
8029
8030         /**
8031          * Perform animation on this element.
8032          * @param {Object} args The YUI animation control args
8033          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8034          * @param {Function} onComplete (optional) Function to call when animation completes
8035          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8036          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8037          * @return {Roo.Element} this
8038          */
8039         animate : function(args, duration, onComplete, easing, animType){
8040             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8041             return this;
8042         },
8043
8044         /*
8045          * @private Internal animation call
8046          */
8047         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8048             animType = animType || 'run';
8049             opt = opt || {};
8050             var anim = Roo.lib.Anim[animType](
8051                 this.dom, args,
8052                 (opt.duration || defaultDur) || .35,
8053                 (opt.easing || defaultEase) || 'easeOut',
8054                 function(){
8055                     Roo.callback(cb, this);
8056                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8057                 },
8058                 this
8059             );
8060             opt.anim = anim;
8061             return anim;
8062         },
8063
8064         // private legacy anim prep
8065         preanim : function(a, i){
8066             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8067         },
8068
8069         /**
8070          * Removes worthless text nodes
8071          * @param {Boolean} forceReclean (optional) By default the element
8072          * keeps track if it has been cleaned already so
8073          * you can call this over and over. However, if you update the element and
8074          * need to force a reclean, you can pass true.
8075          */
8076         clean : function(forceReclean){
8077             if(this.isCleaned && forceReclean !== true){
8078                 return this;
8079             }
8080             var ns = /\S/;
8081             var d = this.dom, n = d.firstChild, ni = -1;
8082             while(n){
8083                 var nx = n.nextSibling;
8084                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8085                     d.removeChild(n);
8086                 }else{
8087                     n.nodeIndex = ++ni;
8088                 }
8089                 n = nx;
8090             }
8091             this.isCleaned = true;
8092             return this;
8093         },
8094
8095         // private
8096         calcOffsetsTo : function(el){
8097             el = Roo.get(el);
8098             var d = el.dom;
8099             var restorePos = false;
8100             if(el.getStyle('position') == 'static'){
8101                 el.position('relative');
8102                 restorePos = true;
8103             }
8104             var x = 0, y =0;
8105             var op = this.dom;
8106             while(op && op != d && op.tagName != 'HTML'){
8107                 x+= op.offsetLeft;
8108                 y+= op.offsetTop;
8109                 op = op.offsetParent;
8110             }
8111             if(restorePos){
8112                 el.position('static');
8113             }
8114             return [x, y];
8115         },
8116
8117         /**
8118          * Scrolls this element into view within the passed container.
8119          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8120          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8121          * @return {Roo.Element} this
8122          */
8123         scrollIntoView : function(container, hscroll){
8124             var c = Roo.getDom(container) || document.body;
8125             var el = this.dom;
8126
8127             var o = this.calcOffsetsTo(c),
8128                 l = o[0],
8129                 t = o[1],
8130                 b = t+el.offsetHeight,
8131                 r = l+el.offsetWidth;
8132
8133             var ch = c.clientHeight;
8134             var ct = parseInt(c.scrollTop, 10);
8135             var cl = parseInt(c.scrollLeft, 10);
8136             var cb = ct + ch;
8137             var cr = cl + c.clientWidth;
8138
8139             if(t < ct){
8140                 c.scrollTop = t;
8141             }else if(b > cb){
8142                 c.scrollTop = b-ch;
8143             }
8144
8145             if(hscroll !== false){
8146                 if(l < cl){
8147                     c.scrollLeft = l;
8148                 }else if(r > cr){
8149                     c.scrollLeft = r-c.clientWidth;
8150                 }
8151             }
8152             return this;
8153         },
8154
8155         // private
8156         scrollChildIntoView : function(child, hscroll){
8157             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8158         },
8159
8160         /**
8161          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8162          * the new height may not be available immediately.
8163          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8164          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8165          * @param {Function} onComplete (optional) Function to call when animation completes
8166          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8167          * @return {Roo.Element} this
8168          */
8169         autoHeight : function(animate, duration, onComplete, easing){
8170             var oldHeight = this.getHeight();
8171             this.clip();
8172             this.setHeight(1); // force clipping
8173             setTimeout(function(){
8174                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8175                 if(!animate){
8176                     this.setHeight(height);
8177                     this.unclip();
8178                     if(typeof onComplete == "function"){
8179                         onComplete();
8180                     }
8181                 }else{
8182                     this.setHeight(oldHeight); // restore original height
8183                     this.setHeight(height, animate, duration, function(){
8184                         this.unclip();
8185                         if(typeof onComplete == "function") { onComplete(); }
8186                     }.createDelegate(this), easing);
8187                 }
8188             }.createDelegate(this), 0);
8189             return this;
8190         },
8191
8192         /**
8193          * Returns true if this element is an ancestor of the passed element
8194          * @param {HTMLElement/String} el The element to check
8195          * @return {Boolean} True if this element is an ancestor of el, else false
8196          */
8197         contains : function(el){
8198             if(!el){return false;}
8199             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8200         },
8201
8202         /**
8203          * Checks whether the element is currently visible using both visibility and display properties.
8204          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8205          * @return {Boolean} True if the element is currently visible, else false
8206          */
8207         isVisible : function(deep) {
8208             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8209             if(deep !== true || !vis){
8210                 return vis;
8211             }
8212             var p = this.dom.parentNode;
8213             while(p && p.tagName.toLowerCase() != "body"){
8214                 if(!Roo.fly(p, '_isVisible').isVisible()){
8215                     return false;
8216                 }
8217                 p = p.parentNode;
8218             }
8219             return true;
8220         },
8221
8222         /**
8223          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8224          * @param {String} selector The CSS selector
8225          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8226          * @return {CompositeElement/CompositeElementLite} The composite element
8227          */
8228         select : function(selector, unique){
8229             return El.select(selector, unique, this.dom);
8230         },
8231
8232         /**
8233          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8234          * @param {String} selector The CSS selector
8235          * @return {Array} An array of the matched nodes
8236          */
8237         query : function(selector, unique){
8238             return Roo.DomQuery.select(selector, this.dom);
8239         },
8240
8241         /**
8242          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8243          * @param {String} selector The CSS selector
8244          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8245          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8246          */
8247         child : function(selector, returnDom){
8248             var n = Roo.DomQuery.selectNode(selector, this.dom);
8249             return returnDom ? n : Roo.get(n);
8250         },
8251
8252         /**
8253          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8254          * @param {String} selector The CSS selector
8255          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8256          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8257          */
8258         down : function(selector, returnDom){
8259             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8260             return returnDom ? n : Roo.get(n);
8261         },
8262
8263         /**
8264          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8265          * @param {String} group The group the DD object is member of
8266          * @param {Object} config The DD config object
8267          * @param {Object} overrides An object containing methods to override/implement on the DD object
8268          * @return {Roo.dd.DD} The DD object
8269          */
8270         initDD : function(group, config, overrides){
8271             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8272             return Roo.apply(dd, overrides);
8273         },
8274
8275         /**
8276          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8277          * @param {String} group The group the DDProxy object is member of
8278          * @param {Object} config The DDProxy config object
8279          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8280          * @return {Roo.dd.DDProxy} The DDProxy object
8281          */
8282         initDDProxy : function(group, config, overrides){
8283             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8284             return Roo.apply(dd, overrides);
8285         },
8286
8287         /**
8288          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8289          * @param {String} group The group the DDTarget object is member of
8290          * @param {Object} config The DDTarget config object
8291          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8292          * @return {Roo.dd.DDTarget} The DDTarget object
8293          */
8294         initDDTarget : function(group, config, overrides){
8295             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8296             return Roo.apply(dd, overrides);
8297         },
8298
8299         /**
8300          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8301          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8302          * @param {Boolean} visible Whether the element is visible
8303          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8304          * @return {Roo.Element} this
8305          */
8306          setVisible : function(visible, animate){
8307             if(!animate || !A){
8308                 if(this.visibilityMode == El.DISPLAY){
8309                     this.setDisplayed(visible);
8310                 }else{
8311                     this.fixDisplay();
8312                     this.dom.style.visibility = visible ? "visible" : "hidden";
8313                 }
8314             }else{
8315                 // closure for composites
8316                 var dom = this.dom;
8317                 var visMode = this.visibilityMode;
8318                 if(visible){
8319                     this.setOpacity(.01);
8320                     this.setVisible(true);
8321                 }
8322                 this.anim({opacity: { to: (visible?1:0) }},
8323                       this.preanim(arguments, 1),
8324                       null, .35, 'easeIn', function(){
8325                          if(!visible){
8326                              if(visMode == El.DISPLAY){
8327                                  dom.style.display = "none";
8328                              }else{
8329                                  dom.style.visibility = "hidden";
8330                              }
8331                              Roo.get(dom).setOpacity(1);
8332                          }
8333                      });
8334             }
8335             return this;
8336         },
8337
8338         /**
8339          * Returns true if display is not "none"
8340          * @return {Boolean}
8341          */
8342         isDisplayed : function() {
8343             return this.getStyle("display") != "none";
8344         },
8345
8346         /**
8347          * Toggles the element's visibility or display, depending on visibility mode.
8348          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8349          * @return {Roo.Element} this
8350          */
8351         toggle : function(animate){
8352             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8353             return this;
8354         },
8355
8356         /**
8357          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8358          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8359          * @return {Roo.Element} this
8360          */
8361         setDisplayed : function(value) {
8362             if(typeof value == "boolean"){
8363                value = value ? this.originalDisplay : "none";
8364             }
8365             this.setStyle("display", value);
8366             return this;
8367         },
8368
8369         /**
8370          * Tries to focus the element. Any exceptions are caught and ignored.
8371          * @return {Roo.Element} this
8372          */
8373         focus : function() {
8374             try{
8375                 this.dom.focus();
8376             }catch(e){}
8377             return this;
8378         },
8379
8380         /**
8381          * Tries to blur the element. Any exceptions are caught and ignored.
8382          * @return {Roo.Element} this
8383          */
8384         blur : function() {
8385             try{
8386                 this.dom.blur();
8387             }catch(e){}
8388             return this;
8389         },
8390
8391         /**
8392          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8393          * @param {String/Array} className The CSS class to add, or an array of classes
8394          * @return {Roo.Element} this
8395          */
8396         addClass : function(className){
8397             if(className instanceof Array){
8398                 for(var i = 0, len = className.length; i < len; i++) {
8399                     this.addClass(className[i]);
8400                 }
8401             }else{
8402                 if(className && !this.hasClass(className)){
8403                     if (this.dom instanceof SVGElement) {
8404                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8405                     } else {
8406                         this.dom.className = this.dom.className + " " + className;
8407                     }
8408                 }
8409             }
8410             return this;
8411         },
8412
8413         /**
8414          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8415          * @param {String/Array} className The CSS class to add, or an array of classes
8416          * @return {Roo.Element} this
8417          */
8418         radioClass : function(className){
8419             var siblings = this.dom.parentNode.childNodes;
8420             for(var i = 0; i < siblings.length; i++) {
8421                 var s = siblings[i];
8422                 if(s.nodeType == 1){
8423                     Roo.get(s).removeClass(className);
8424                 }
8425             }
8426             this.addClass(className);
8427             return this;
8428         },
8429
8430         /**
8431          * Removes one or more CSS classes from the element.
8432          * @param {String/Array} className The CSS class to remove, or an array of classes
8433          * @return {Roo.Element} this
8434          */
8435         removeClass : function(className){
8436             
8437             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8438             if(!className || !cn){
8439                 return this;
8440             }
8441             if(className instanceof Array){
8442                 for(var i = 0, len = className.length; i < len; i++) {
8443                     this.removeClass(className[i]);
8444                 }
8445             }else{
8446                 if(this.hasClass(className)){
8447                     var re = this.classReCache[className];
8448                     if (!re) {
8449                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8450                        this.classReCache[className] = re;
8451                     }
8452                     if (this.dom instanceof SVGElement) {
8453                         this.dom.className.baseVal = cn.replace(re, " ");
8454                     } else {
8455                         this.dom.className = cn.replace(re, " ");
8456                     }
8457                 }
8458             }
8459             return this;
8460         },
8461
8462         // private
8463         classReCache: {},
8464
8465         /**
8466          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8467          * @param {String} className The CSS class to toggle
8468          * @return {Roo.Element} this
8469          */
8470         toggleClass : function(className){
8471             if(this.hasClass(className)){
8472                 this.removeClass(className);
8473             }else{
8474                 this.addClass(className);
8475             }
8476             return this;
8477         },
8478
8479         /**
8480          * Checks if the specified CSS class exists on this element's DOM node.
8481          * @param {String} className The CSS class to check for
8482          * @return {Boolean} True if the class exists, else false
8483          */
8484         hasClass : function(className){
8485             if (this.dom instanceof SVGElement) {
8486                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8487             } 
8488             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8489         },
8490
8491         /**
8492          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8493          * @param {String} oldClassName The CSS class to replace
8494          * @param {String} newClassName The replacement CSS class
8495          * @return {Roo.Element} this
8496          */
8497         replaceClass : function(oldClassName, newClassName){
8498             this.removeClass(oldClassName);
8499             this.addClass(newClassName);
8500             return this;
8501         },
8502
8503         /**
8504          * Returns an object with properties matching the styles requested.
8505          * For example, el.getStyles('color', 'font-size', 'width') might return
8506          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8507          * @param {String} style1 A style name
8508          * @param {String} style2 A style name
8509          * @param {String} etc.
8510          * @return {Object} The style object
8511          */
8512         getStyles : function(){
8513             var a = arguments, len = a.length, r = {};
8514             for(var i = 0; i < len; i++){
8515                 r[a[i]] = this.getStyle(a[i]);
8516             }
8517             return r;
8518         },
8519
8520         /**
8521          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8522          * @param {String} property The style property whose value is returned.
8523          * @return {String} The current value of the style property for this element.
8524          */
8525         getStyle : function(){
8526             return view && view.getComputedStyle ?
8527                 function(prop){
8528                     var el = this.dom, v, cs, camel;
8529                     if(prop == 'float'){
8530                         prop = "cssFloat";
8531                     }
8532                     if(el.style && (v = el.style[prop])){
8533                         return v;
8534                     }
8535                     if(cs = view.getComputedStyle(el, "")){
8536                         if(!(camel = propCache[prop])){
8537                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8538                         }
8539                         return cs[camel];
8540                     }
8541                     return null;
8542                 } :
8543                 function(prop){
8544                     var el = this.dom, v, cs, camel;
8545                     if(prop == 'opacity'){
8546                         if(typeof el.style.filter == 'string'){
8547                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8548                             if(m){
8549                                 var fv = parseFloat(m[1]);
8550                                 if(!isNaN(fv)){
8551                                     return fv ? fv / 100 : 0;
8552                                 }
8553                             }
8554                         }
8555                         return 1;
8556                     }else if(prop == 'float'){
8557                         prop = "styleFloat";
8558                     }
8559                     if(!(camel = propCache[prop])){
8560                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8561                     }
8562                     if(v = el.style[camel]){
8563                         return v;
8564                     }
8565                     if(cs = el.currentStyle){
8566                         return cs[camel];
8567                     }
8568                     return null;
8569                 };
8570         }(),
8571
8572         /**
8573          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8574          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8575          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8576          * @return {Roo.Element} this
8577          */
8578         setStyle : function(prop, value){
8579             if(typeof prop == "string"){
8580                 
8581                 if (prop == 'float') {
8582                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8583                     return this;
8584                 }
8585                 
8586                 var camel;
8587                 if(!(camel = propCache[prop])){
8588                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8589                 }
8590                 
8591                 if(camel == 'opacity') {
8592                     this.setOpacity(value);
8593                 }else{
8594                     this.dom.style[camel] = value;
8595                 }
8596             }else{
8597                 for(var style in prop){
8598                     if(typeof prop[style] != "function"){
8599                        this.setStyle(style, prop[style]);
8600                     }
8601                 }
8602             }
8603             return this;
8604         },
8605
8606         /**
8607          * More flexible version of {@link #setStyle} for setting style properties.
8608          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8609          * a function which returns such a specification.
8610          * @return {Roo.Element} this
8611          */
8612         applyStyles : function(style){
8613             Roo.DomHelper.applyStyles(this.dom, style);
8614             return this;
8615         },
8616
8617         /**
8618           * 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).
8619           * @return {Number} The X position of the element
8620           */
8621         getX : function(){
8622             return D.getX(this.dom);
8623         },
8624
8625         /**
8626           * 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).
8627           * @return {Number} The Y position of the element
8628           */
8629         getY : function(){
8630             return D.getY(this.dom);
8631         },
8632
8633         /**
8634           * 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).
8635           * @return {Array} The XY position of the element
8636           */
8637         getXY : function(){
8638             return D.getXY(this.dom);
8639         },
8640
8641         /**
8642          * 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).
8643          * @param {Number} The X position of the element
8644          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8645          * @return {Roo.Element} this
8646          */
8647         setX : function(x, animate){
8648             if(!animate || !A){
8649                 D.setX(this.dom, x);
8650             }else{
8651                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8652             }
8653             return this;
8654         },
8655
8656         /**
8657          * 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).
8658          * @param {Number} The Y position of the element
8659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8660          * @return {Roo.Element} this
8661          */
8662         setY : function(y, animate){
8663             if(!animate || !A){
8664                 D.setY(this.dom, y);
8665             }else{
8666                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8667             }
8668             return this;
8669         },
8670
8671         /**
8672          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8673          * @param {String} left The left CSS property value
8674          * @return {Roo.Element} this
8675          */
8676         setLeft : function(left){
8677             this.setStyle("left", this.addUnits(left));
8678             return this;
8679         },
8680
8681         /**
8682          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8683          * @param {String} top The top CSS property value
8684          * @return {Roo.Element} this
8685          */
8686         setTop : function(top){
8687             this.setStyle("top", this.addUnits(top));
8688             return this;
8689         },
8690
8691         /**
8692          * Sets the element's CSS right style.
8693          * @param {String} right The right CSS property value
8694          * @return {Roo.Element} this
8695          */
8696         setRight : function(right){
8697             this.setStyle("right", this.addUnits(right));
8698             return this;
8699         },
8700
8701         /**
8702          * Sets the element's CSS bottom style.
8703          * @param {String} bottom The bottom CSS property value
8704          * @return {Roo.Element} this
8705          */
8706         setBottom : function(bottom){
8707             this.setStyle("bottom", this.addUnits(bottom));
8708             return this;
8709         },
8710
8711         /**
8712          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8713          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8714          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8715          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8716          * @return {Roo.Element} this
8717          */
8718         setXY : function(pos, animate){
8719             if(!animate || !A){
8720                 D.setXY(this.dom, pos);
8721             }else{
8722                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8723             }
8724             return this;
8725         },
8726
8727         /**
8728          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8729          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8730          * @param {Number} x X value for new position (coordinates are page-based)
8731          * @param {Number} y Y value for new position (coordinates are page-based)
8732          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8733          * @return {Roo.Element} this
8734          */
8735         setLocation : function(x, y, animate){
8736             this.setXY([x, y], this.preanim(arguments, 2));
8737             return this;
8738         },
8739
8740         /**
8741          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8742          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8743          * @param {Number} x X value for new position (coordinates are page-based)
8744          * @param {Number} y Y value for new position (coordinates are page-based)
8745          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8746          * @return {Roo.Element} this
8747          */
8748         moveTo : function(x, y, animate){
8749             this.setXY([x, y], this.preanim(arguments, 2));
8750             return this;
8751         },
8752
8753         /**
8754          * Returns the region of the given element.
8755          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8756          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8757          */
8758         getRegion : function(){
8759             return D.getRegion(this.dom);
8760         },
8761
8762         /**
8763          * Returns the offset height of the element
8764          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8765          * @return {Number} The element's height
8766          */
8767         getHeight : function(contentHeight){
8768             var h = this.dom.offsetHeight || 0;
8769             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8770         },
8771
8772         /**
8773          * Returns the offset width of the element
8774          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8775          * @return {Number} The element's width
8776          */
8777         getWidth : function(contentWidth){
8778             var w = this.dom.offsetWidth || 0;
8779             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8780         },
8781
8782         /**
8783          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8784          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8785          * if a height has not been set using CSS.
8786          * @return {Number}
8787          */
8788         getComputedHeight : function(){
8789             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8790             if(!h){
8791                 h = parseInt(this.getStyle('height'), 10) || 0;
8792                 if(!this.isBorderBox()){
8793                     h += this.getFrameWidth('tb');
8794                 }
8795             }
8796             return h;
8797         },
8798
8799         /**
8800          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8801          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8802          * if a width has not been set using CSS.
8803          * @return {Number}
8804          */
8805         getComputedWidth : function(){
8806             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8807             if(!w){
8808                 w = parseInt(this.getStyle('width'), 10) || 0;
8809                 if(!this.isBorderBox()){
8810                     w += this.getFrameWidth('lr');
8811                 }
8812             }
8813             return w;
8814         },
8815
8816         /**
8817          * Returns the size of the element.
8818          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8819          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8820          */
8821         getSize : function(contentSize){
8822             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8823         },
8824
8825         /**
8826          * Returns the width and height of the viewport.
8827          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8828          */
8829         getViewSize : function(){
8830             var d = this.dom, doc = document, aw = 0, ah = 0;
8831             if(d == doc || d == doc.body){
8832                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8833             }else{
8834                 return {
8835                     width : d.clientWidth,
8836                     height: d.clientHeight
8837                 };
8838             }
8839         },
8840
8841         /**
8842          * Returns the value of the "value" attribute
8843          * @param {Boolean} asNumber true to parse the value as a number
8844          * @return {String/Number}
8845          */
8846         getValue : function(asNumber){
8847             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8848         },
8849
8850         // private
8851         adjustWidth : function(width){
8852             if(typeof width == "number"){
8853                 if(this.autoBoxAdjust && !this.isBorderBox()){
8854                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8855                 }
8856                 if(width < 0){
8857                     width = 0;
8858                 }
8859             }
8860             return width;
8861         },
8862
8863         // private
8864         adjustHeight : function(height){
8865             if(typeof height == "number"){
8866                if(this.autoBoxAdjust && !this.isBorderBox()){
8867                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8868                }
8869                if(height < 0){
8870                    height = 0;
8871                }
8872             }
8873             return height;
8874         },
8875
8876         /**
8877          * Set the width of the element
8878          * @param {Number} width The new width
8879          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8880          * @return {Roo.Element} this
8881          */
8882         setWidth : function(width, animate){
8883             width = this.adjustWidth(width);
8884             if(!animate || !A){
8885                 this.dom.style.width = this.addUnits(width);
8886             }else{
8887                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8888             }
8889             return this;
8890         },
8891
8892         /**
8893          * Set the height of the element
8894          * @param {Number} height The new height
8895          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8896          * @return {Roo.Element} this
8897          */
8898          setHeight : function(height, animate){
8899             height = this.adjustHeight(height);
8900             if(!animate || !A){
8901                 this.dom.style.height = this.addUnits(height);
8902             }else{
8903                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8904             }
8905             return this;
8906         },
8907
8908         /**
8909          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8910          * @param {Number} width The new width
8911          * @param {Number} height The new height
8912          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8913          * @return {Roo.Element} this
8914          */
8915          setSize : function(width, height, animate){
8916             if(typeof width == "object"){ // in case of object from getSize()
8917                 height = width.height; width = width.width;
8918             }
8919             width = this.adjustWidth(width); height = this.adjustHeight(height);
8920             if(!animate || !A){
8921                 this.dom.style.width = this.addUnits(width);
8922                 this.dom.style.height = this.addUnits(height);
8923             }else{
8924                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8925             }
8926             return this;
8927         },
8928
8929         /**
8930          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8931          * @param {Number} x X value for new position (coordinates are page-based)
8932          * @param {Number} y Y value for new position (coordinates are page-based)
8933          * @param {Number} width The new width
8934          * @param {Number} height The new height
8935          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8936          * @return {Roo.Element} this
8937          */
8938         setBounds : function(x, y, width, height, animate){
8939             if(!animate || !A){
8940                 this.setSize(width, height);
8941                 this.setLocation(x, y);
8942             }else{
8943                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8944                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8945                               this.preanim(arguments, 4), 'motion');
8946             }
8947             return this;
8948         },
8949
8950         /**
8951          * 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.
8952          * @param {Roo.lib.Region} region The region to fill
8953          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8954          * @return {Roo.Element} this
8955          */
8956         setRegion : function(region, animate){
8957             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8958             return this;
8959         },
8960
8961         /**
8962          * Appends an event handler
8963          *
8964          * @param {String}   eventName     The type of event to append
8965          * @param {Function} fn        The method the event invokes
8966          * @param {Object} scope       (optional) The scope (this object) of the fn
8967          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8968          */
8969         addListener : function(eventName, fn, scope, options)
8970         {
8971             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8972                 this.addListener('touchstart', this.onTapHandler, this);
8973             }
8974             
8975             // we need to handle a special case where dom element is a svg element.
8976             // in this case we do not actua
8977             if (!this.dom) {
8978                 return;
8979             }
8980             
8981             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8982                 if (typeof(this.listeners[eventName]) == 'undefined') {
8983                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8984                 }
8985                 this.listeners[eventName].addListener(fn, scope, options);
8986                 return;
8987             }
8988             
8989                 
8990             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8991             
8992             
8993         },
8994         tapedTwice : false,
8995         onTapHandler : function(event)
8996         {
8997             if(!this.tapedTwice) {
8998                 this.tapedTwice = true;
8999                 var s = this;
9000                 setTimeout( function() {
9001                     s.tapedTwice = false;
9002                 }, 300 );
9003                 return;
9004             }
9005             event.preventDefault();
9006             var revent = new MouseEvent('dblclick',  {
9007                 view: window,
9008                 bubbles: true,
9009                 cancelable: true
9010             });
9011              
9012             this.dom.dispatchEvent(revent);
9013             //action on double tap goes below
9014              
9015         }, 
9016  
9017         /**
9018          * Removes an event handler from this element
9019          * @param {String} eventName the type of event to remove
9020          * @param {Function} fn the method the event invokes
9021          * @param {Function} scope (needed for svg fake listeners)
9022          * @return {Roo.Element} this
9023          */
9024         removeListener : function(eventName, fn, scope){
9025             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9026             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9027                 return this;
9028             }
9029             this.listeners[eventName].removeListener(fn, scope);
9030             return this;
9031         },
9032
9033         /**
9034          * Removes all previous added listeners from this element
9035          * @return {Roo.Element} this
9036          */
9037         removeAllListeners : function(){
9038             E.purgeElement(this.dom);
9039             this.listeners = {};
9040             return this;
9041         },
9042
9043         relayEvent : function(eventName, observable){
9044             this.on(eventName, function(e){
9045                 observable.fireEvent(eventName, e);
9046             });
9047         },
9048
9049         
9050         /**
9051          * Set the opacity of the element
9052          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9053          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9054          * @return {Roo.Element} this
9055          */
9056          setOpacity : function(opacity, animate){
9057             if(!animate || !A){
9058                 var s = this.dom.style;
9059                 if(Roo.isIE){
9060                     s.zoom = 1;
9061                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9062                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9063                 }else{
9064                     s.opacity = opacity;
9065                 }
9066             }else{
9067                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9068             }
9069             return this;
9070         },
9071
9072         /**
9073          * Gets the left X coordinate
9074          * @param {Boolean} local True to get the local css position instead of page coordinate
9075          * @return {Number}
9076          */
9077         getLeft : function(local){
9078             if(!local){
9079                 return this.getX();
9080             }else{
9081                 return parseInt(this.getStyle("left"), 10) || 0;
9082             }
9083         },
9084
9085         /**
9086          * Gets the right X coordinate of the element (element X position + element width)
9087          * @param {Boolean} local True to get the local css position instead of page coordinate
9088          * @return {Number}
9089          */
9090         getRight : function(local){
9091             if(!local){
9092                 return this.getX() + this.getWidth();
9093             }else{
9094                 return (this.getLeft(true) + this.getWidth()) || 0;
9095             }
9096         },
9097
9098         /**
9099          * Gets the top Y coordinate
9100          * @param {Boolean} local True to get the local css position instead of page coordinate
9101          * @return {Number}
9102          */
9103         getTop : function(local) {
9104             if(!local){
9105                 return this.getY();
9106             }else{
9107                 return parseInt(this.getStyle("top"), 10) || 0;
9108             }
9109         },
9110
9111         /**
9112          * Gets the bottom Y coordinate of the element (element Y position + element height)
9113          * @param {Boolean} local True to get the local css position instead of page coordinate
9114          * @return {Number}
9115          */
9116         getBottom : function(local){
9117             if(!local){
9118                 return this.getY() + this.getHeight();
9119             }else{
9120                 return (this.getTop(true) + this.getHeight()) || 0;
9121             }
9122         },
9123
9124         /**
9125         * Initializes positioning on this element. If a desired position is not passed, it will make the
9126         * the element positioned relative IF it is not already positioned.
9127         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9128         * @param {Number} zIndex (optional) The zIndex to apply
9129         * @param {Number} x (optional) Set the page X position
9130         * @param {Number} y (optional) Set the page Y position
9131         */
9132         position : function(pos, zIndex, x, y){
9133             if(!pos){
9134                if(this.getStyle('position') == 'static'){
9135                    this.setStyle('position', 'relative');
9136                }
9137             }else{
9138                 this.setStyle("position", pos);
9139             }
9140             if(zIndex){
9141                 this.setStyle("z-index", zIndex);
9142             }
9143             if(x !== undefined && y !== undefined){
9144                 this.setXY([x, y]);
9145             }else if(x !== undefined){
9146                 this.setX(x);
9147             }else if(y !== undefined){
9148                 this.setY(y);
9149             }
9150         },
9151
9152         /**
9153         * Clear positioning back to the default when the document was loaded
9154         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9155         * @return {Roo.Element} this
9156          */
9157         clearPositioning : function(value){
9158             value = value ||'';
9159             this.setStyle({
9160                 "left": value,
9161                 "right": value,
9162                 "top": value,
9163                 "bottom": value,
9164                 "z-index": "",
9165                 "position" : "static"
9166             });
9167             return this;
9168         },
9169
9170         /**
9171         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9172         * snapshot before performing an update and then restoring the element.
9173         * @return {Object}
9174         */
9175         getPositioning : function(){
9176             var l = this.getStyle("left");
9177             var t = this.getStyle("top");
9178             return {
9179                 "position" : this.getStyle("position"),
9180                 "left" : l,
9181                 "right" : l ? "" : this.getStyle("right"),
9182                 "top" : t,
9183                 "bottom" : t ? "" : this.getStyle("bottom"),
9184                 "z-index" : this.getStyle("z-index")
9185             };
9186         },
9187
9188         /**
9189          * Gets the width of the border(s) for the specified side(s)
9190          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9191          * passing lr would get the border (l)eft width + the border (r)ight width.
9192          * @return {Number} The width of the sides passed added together
9193          */
9194         getBorderWidth : function(side){
9195             return this.addStyles(side, El.borders);
9196         },
9197
9198         /**
9199          * Gets the width of the padding(s) for the specified side(s)
9200          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9201          * passing lr would get the padding (l)eft + the padding (r)ight.
9202          * @return {Number} The padding of the sides passed added together
9203          */
9204         getPadding : function(side){
9205             return this.addStyles(side, El.paddings);
9206         },
9207
9208         /**
9209         * Set positioning with an object returned by getPositioning().
9210         * @param {Object} posCfg
9211         * @return {Roo.Element} this
9212          */
9213         setPositioning : function(pc){
9214             this.applyStyles(pc);
9215             if(pc.right == "auto"){
9216                 this.dom.style.right = "";
9217             }
9218             if(pc.bottom == "auto"){
9219                 this.dom.style.bottom = "";
9220             }
9221             return this;
9222         },
9223
9224         // private
9225         fixDisplay : function(){
9226             if(this.getStyle("display") == "none"){
9227                 this.setStyle("visibility", "hidden");
9228                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9229                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9230                     this.setStyle("display", "block");
9231                 }
9232             }
9233         },
9234
9235         /**
9236          * Quick set left and top adding default units
9237          * @param {String} left The left CSS property value
9238          * @param {String} top The top CSS property value
9239          * @return {Roo.Element} this
9240          */
9241          setLeftTop : function(left, top){
9242             this.dom.style.left = this.addUnits(left);
9243             this.dom.style.top = this.addUnits(top);
9244             return this;
9245         },
9246
9247         /**
9248          * Move this element relative to its current position.
9249          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9250          * @param {Number} distance How far to move the element in pixels
9251          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9252          * @return {Roo.Element} this
9253          */
9254          move : function(direction, distance, animate){
9255             var xy = this.getXY();
9256             direction = direction.toLowerCase();
9257             switch(direction){
9258                 case "l":
9259                 case "left":
9260                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9261                     break;
9262                case "r":
9263                case "right":
9264                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9265                     break;
9266                case "t":
9267                case "top":
9268                case "up":
9269                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9270                     break;
9271                case "b":
9272                case "bottom":
9273                case "down":
9274                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9275                     break;
9276             }
9277             return this;
9278         },
9279
9280         /**
9281          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9282          * @return {Roo.Element} this
9283          */
9284         clip : function(){
9285             if(!this.isClipped){
9286                this.isClipped = true;
9287                this.originalClip = {
9288                    "o": this.getStyle("overflow"),
9289                    "x": this.getStyle("overflow-x"),
9290                    "y": this.getStyle("overflow-y")
9291                };
9292                this.setStyle("overflow", "hidden");
9293                this.setStyle("overflow-x", "hidden");
9294                this.setStyle("overflow-y", "hidden");
9295             }
9296             return this;
9297         },
9298
9299         /**
9300          *  Return clipping (overflow) to original clipping before clip() was called
9301          * @return {Roo.Element} this
9302          */
9303         unclip : function(){
9304             if(this.isClipped){
9305                 this.isClipped = false;
9306                 var o = this.originalClip;
9307                 if(o.o){this.setStyle("overflow", o.o);}
9308                 if(o.x){this.setStyle("overflow-x", o.x);}
9309                 if(o.y){this.setStyle("overflow-y", o.y);}
9310             }
9311             return this;
9312         },
9313
9314
9315         /**
9316          * Gets the x,y coordinates specified by the anchor position on the element.
9317          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9318          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9319          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9320          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9321          * @return {Array} [x, y] An array containing the element's x and y coordinates
9322          */
9323         getAnchorXY : function(anchor, local, s){
9324             //Passing a different size is useful for pre-calculating anchors,
9325             //especially for anchored animations that change the el size.
9326
9327             var w, h, vp = false;
9328             if(!s){
9329                 var d = this.dom;
9330                 if(d == document.body || d == document){
9331                     vp = true;
9332                     w = D.getViewWidth(); h = D.getViewHeight();
9333                 }else{
9334                     w = this.getWidth(); h = this.getHeight();
9335                 }
9336             }else{
9337                 w = s.width;  h = s.height;
9338             }
9339             var x = 0, y = 0, r = Math.round;
9340             switch((anchor || "tl").toLowerCase()){
9341                 case "c":
9342                     x = r(w*.5);
9343                     y = r(h*.5);
9344                 break;
9345                 case "t":
9346                     x = r(w*.5);
9347                     y = 0;
9348                 break;
9349                 case "l":
9350                     x = 0;
9351                     y = r(h*.5);
9352                 break;
9353                 case "r":
9354                     x = w;
9355                     y = r(h*.5);
9356                 break;
9357                 case "b":
9358                     x = r(w*.5);
9359                     y = h;
9360                 break;
9361                 case "tl":
9362                     x = 0;
9363                     y = 0;
9364                 break;
9365                 case "bl":
9366                     x = 0;
9367                     y = h;
9368                 break;
9369                 case "br":
9370                     x = w;
9371                     y = h;
9372                 break;
9373                 case "tr":
9374                     x = w;
9375                     y = 0;
9376                 break;
9377             }
9378             if(local === true){
9379                 return [x, y];
9380             }
9381             if(vp){
9382                 var sc = this.getScroll();
9383                 return [x + sc.left, y + sc.top];
9384             }
9385             //Add the element's offset xy
9386             var o = this.getXY();
9387             return [x+o[0], y+o[1]];
9388         },
9389
9390         /**
9391          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9392          * supported position values.
9393          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9394          * @param {String} position The position to align to.
9395          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9396          * @return {Array} [x, y]
9397          */
9398         getAlignToXY : function(el, p, o)
9399         {
9400             el = Roo.get(el);
9401             var d = this.dom;
9402             if(!el.dom){
9403                 throw "Element.alignTo with an element that doesn't exist";
9404             }
9405             var c = false; //constrain to viewport
9406             var p1 = "", p2 = "";
9407             o = o || [0,0];
9408
9409             if(!p){
9410                 p = "tl-bl";
9411             }else if(p == "?"){
9412                 p = "tl-bl?";
9413             }else if(p.indexOf("-") == -1){
9414                 p = "tl-" + p;
9415             }
9416             p = p.toLowerCase();
9417             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9418             if(!m){
9419                throw "Element.alignTo with an invalid alignment " + p;
9420             }
9421             p1 = m[1]; p2 = m[2]; c = !!m[3];
9422
9423             //Subtract the aligned el's internal xy from the target's offset xy
9424             //plus custom offset to get the aligned el's new offset xy
9425             var a1 = this.getAnchorXY(p1, true);
9426             var a2 = el.getAnchorXY(p2, false);
9427             var x = a2[0] - a1[0] + o[0];
9428             var y = a2[1] - a1[1] + o[1];
9429             if(c){
9430                 //constrain the aligned el to viewport if necessary
9431                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9432                 // 5px of margin for ie
9433                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9434
9435                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9436                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9437                 //otherwise swap the aligned el to the opposite border of the target.
9438                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9439                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9440                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9441                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9442
9443                var doc = document;
9444                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9445                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9446
9447                if((x+w) > dw + scrollX){
9448                     x = swapX ? r.left-w : dw+scrollX-w;
9449                 }
9450                if(x < scrollX){
9451                    x = swapX ? r.right : scrollX;
9452                }
9453                if((y+h) > dh + scrollY){
9454                     y = swapY ? r.top-h : dh+scrollY-h;
9455                 }
9456                if (y < scrollY){
9457                    y = swapY ? r.bottom : scrollY;
9458                }
9459             }
9460             return [x,y];
9461         },
9462
9463         // private
9464         getConstrainToXY : function(){
9465             var os = {top:0, left:0, bottom:0, right: 0};
9466
9467             return function(el, local, offsets, proposedXY){
9468                 el = Roo.get(el);
9469                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9470
9471                 var vw, vh, vx = 0, vy = 0;
9472                 if(el.dom == document.body || el.dom == document){
9473                     vw = Roo.lib.Dom.getViewWidth();
9474                     vh = Roo.lib.Dom.getViewHeight();
9475                 }else{
9476                     vw = el.dom.clientWidth;
9477                     vh = el.dom.clientHeight;
9478                     if(!local){
9479                         var vxy = el.getXY();
9480                         vx = vxy[0];
9481                         vy = vxy[1];
9482                     }
9483                 }
9484
9485                 var s = el.getScroll();
9486
9487                 vx += offsets.left + s.left;
9488                 vy += offsets.top + s.top;
9489
9490                 vw -= offsets.right;
9491                 vh -= offsets.bottom;
9492
9493                 var vr = vx+vw;
9494                 var vb = vy+vh;
9495
9496                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9497                 var x = xy[0], y = xy[1];
9498                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9499
9500                 // only move it if it needs it
9501                 var moved = false;
9502
9503                 // first validate right/bottom
9504                 if((x + w) > vr){
9505                     x = vr - w;
9506                     moved = true;
9507                 }
9508                 if((y + h) > vb){
9509                     y = vb - h;
9510                     moved = true;
9511                 }
9512                 // then make sure top/left isn't negative
9513                 if(x < vx){
9514                     x = vx;
9515                     moved = true;
9516                 }
9517                 if(y < vy){
9518                     y = vy;
9519                     moved = true;
9520                 }
9521                 return moved ? [x, y] : false;
9522             };
9523         }(),
9524
9525         // private
9526         adjustForConstraints : function(xy, parent, offsets){
9527             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9528         },
9529
9530         /**
9531          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9532          * document it aligns it to the viewport.
9533          * The position parameter is optional, and can be specified in any one of the following formats:
9534          * <ul>
9535          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9536          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9537          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9538          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9539          *   <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
9540          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9541          * </ul>
9542          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9543          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9544          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9545          * that specified in order to enforce the viewport constraints.
9546          * Following are all of the supported anchor positions:
9547     <pre>
9548     Value  Description
9549     -----  -----------------------------
9550     tl     The top left corner (default)
9551     t      The center of the top edge
9552     tr     The top right corner
9553     l      The center of the left edge
9554     c      In the center of the element
9555     r      The center of the right edge
9556     bl     The bottom left corner
9557     b      The center of the bottom edge
9558     br     The bottom right corner
9559     </pre>
9560     Example Usage:
9561     <pre><code>
9562     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9563     el.alignTo("other-el");
9564
9565     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9566     el.alignTo("other-el", "tr?");
9567
9568     // align the bottom right corner of el with the center left edge of other-el
9569     el.alignTo("other-el", "br-l?");
9570
9571     // align the center of el with the bottom left corner of other-el and
9572     // adjust the x position by -6 pixels (and the y position by 0)
9573     el.alignTo("other-el", "c-bl", [-6, 0]);
9574     </code></pre>
9575          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9576          * @param {String} position The position to align to.
9577          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9578          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9579          * @return {Roo.Element} this
9580          */
9581         alignTo : function(element, position, offsets, animate){
9582             var xy = this.getAlignToXY(element, position, offsets);
9583             this.setXY(xy, this.preanim(arguments, 3));
9584             return this;
9585         },
9586
9587         /**
9588          * Anchors an element to another element and realigns it when the window is resized.
9589          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9590          * @param {String} position The position to align to.
9591          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9592          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9593          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9594          * is a number, it is used as the buffer delay (defaults to 50ms).
9595          * @param {Function} callback The function to call after the animation finishes
9596          * @return {Roo.Element} this
9597          */
9598         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9599             var action = function(){
9600                 this.alignTo(el, alignment, offsets, animate);
9601                 Roo.callback(callback, this);
9602             };
9603             Roo.EventManager.onWindowResize(action, this);
9604             var tm = typeof monitorScroll;
9605             if(tm != 'undefined'){
9606                 Roo.EventManager.on(window, 'scroll', action, this,
9607                     {buffer: tm == 'number' ? monitorScroll : 50});
9608             }
9609             action.call(this); // align immediately
9610             return this;
9611         },
9612         /**
9613          * Clears any opacity settings from this element. Required in some cases for IE.
9614          * @return {Roo.Element} this
9615          */
9616         clearOpacity : function(){
9617             if (window.ActiveXObject) {
9618                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9619                     this.dom.style.filter = "";
9620                 }
9621             } else {
9622                 this.dom.style.opacity = "";
9623                 this.dom.style["-moz-opacity"] = "";
9624                 this.dom.style["-khtml-opacity"] = "";
9625             }
9626             return this;
9627         },
9628
9629         /**
9630          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9631          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9632          * @return {Roo.Element} this
9633          */
9634         hide : function(animate){
9635             this.setVisible(false, this.preanim(arguments, 0));
9636             return this;
9637         },
9638
9639         /**
9640         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9641         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9642          * @return {Roo.Element} this
9643          */
9644         show : function(animate){
9645             this.setVisible(true, this.preanim(arguments, 0));
9646             return this;
9647         },
9648
9649         /**
9650          * @private Test if size has a unit, otherwise appends the default
9651          */
9652         addUnits : function(size){
9653             return Roo.Element.addUnits(size, this.defaultUnit);
9654         },
9655
9656         /**
9657          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9658          * @return {Roo.Element} this
9659          */
9660         beginMeasure : function(){
9661             var el = this.dom;
9662             if(el.offsetWidth || el.offsetHeight){
9663                 return this; // offsets work already
9664             }
9665             var changed = [];
9666             var p = this.dom, b = document.body; // start with this element
9667             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9668                 var pe = Roo.get(p);
9669                 if(pe.getStyle('display') == 'none'){
9670                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9671                     p.style.visibility = "hidden";
9672                     p.style.display = "block";
9673                 }
9674                 p = p.parentNode;
9675             }
9676             this._measureChanged = changed;
9677             return this;
9678
9679         },
9680
9681         /**
9682          * Restores displays to before beginMeasure was called
9683          * @return {Roo.Element} this
9684          */
9685         endMeasure : function(){
9686             var changed = this._measureChanged;
9687             if(changed){
9688                 for(var i = 0, len = changed.length; i < len; i++) {
9689                     var r = changed[i];
9690                     r.el.style.visibility = r.visibility;
9691                     r.el.style.display = "none";
9692                 }
9693                 this._measureChanged = null;
9694             }
9695             return this;
9696         },
9697
9698         /**
9699         * Update the innerHTML of this element, optionally searching for and processing scripts
9700         * @param {String} html The new HTML
9701         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9702         * @param {Function} callback For async script loading you can be noticed when the update completes
9703         * @return {Roo.Element} this
9704          */
9705         update : function(html, loadScripts, callback){
9706             if(typeof html == "undefined"){
9707                 html = "";
9708             }
9709             if(loadScripts !== true){
9710                 this.dom.innerHTML = html;
9711                 if(typeof callback == "function"){
9712                     callback();
9713                 }
9714                 return this;
9715             }
9716             var id = Roo.id();
9717             var dom = this.dom;
9718
9719             html += '<span id="' + id + '"></span>';
9720
9721             E.onAvailable(id, function(){
9722                 var hd = document.getElementsByTagName("head")[0];
9723                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9724                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9725                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9726
9727                 var match;
9728                 while(match = re.exec(html)){
9729                     var attrs = match[1];
9730                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9731                     if(srcMatch && srcMatch[2]){
9732                        var s = document.createElement("script");
9733                        s.src = srcMatch[2];
9734                        var typeMatch = attrs.match(typeRe);
9735                        if(typeMatch && typeMatch[2]){
9736                            s.type = typeMatch[2];
9737                        }
9738                        hd.appendChild(s);
9739                     }else if(match[2] && match[2].length > 0){
9740                         if(window.execScript) {
9741                            window.execScript(match[2]);
9742                         } else {
9743                             /**
9744                              * eval:var:id
9745                              * eval:var:dom
9746                              * eval:var:html
9747                              * 
9748                              */
9749                            window.eval(match[2]);
9750                         }
9751                     }
9752                 }
9753                 var el = document.getElementById(id);
9754                 if(el){el.parentNode.removeChild(el);}
9755                 if(typeof callback == "function"){
9756                     callback();
9757                 }
9758             });
9759             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9760             return this;
9761         },
9762
9763         /**
9764          * Direct access to the UpdateManager update() method (takes the same parameters).
9765          * @param {String/Function} url The url for this request or a function to call to get the url
9766          * @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}
9767          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9768          * @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.
9769          * @return {Roo.Element} this
9770          */
9771         load : function(){
9772             var um = this.getUpdateManager();
9773             um.update.apply(um, arguments);
9774             return this;
9775         },
9776
9777         /**
9778         * Gets this element's UpdateManager
9779         * @return {Roo.UpdateManager} The UpdateManager
9780         */
9781         getUpdateManager : function(){
9782             if(!this.updateManager){
9783                 this.updateManager = new Roo.UpdateManager(this);
9784             }
9785             return this.updateManager;
9786         },
9787
9788         /**
9789          * Disables text selection for this element (normalized across browsers)
9790          * @return {Roo.Element} this
9791          */
9792         unselectable : function(){
9793             this.dom.unselectable = "on";
9794             this.swallowEvent("selectstart", true);
9795             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9796             this.addClass("x-unselectable");
9797             return this;
9798         },
9799
9800         /**
9801         * Calculates the x, y to center this element on the screen
9802         * @return {Array} The x, y values [x, y]
9803         */
9804         getCenterXY : function(){
9805             return this.getAlignToXY(document, 'c-c');
9806         },
9807
9808         /**
9809         * Centers the Element in either the viewport, or another Element.
9810         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9811         */
9812         center : function(centerIn){
9813             this.alignTo(centerIn || document, 'c-c');
9814             return this;
9815         },
9816
9817         /**
9818          * Tests various css rules/browsers to determine if this element uses a border box
9819          * @return {Boolean}
9820          */
9821         isBorderBox : function(){
9822             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9823         },
9824
9825         /**
9826          * Return a box {x, y, width, height} that can be used to set another elements
9827          * size/location to match this element.
9828          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9829          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9830          * @return {Object} box An object in the format {x, y, width, height}
9831          */
9832         getBox : function(contentBox, local){
9833             var xy;
9834             if(!local){
9835                 xy = this.getXY();
9836             }else{
9837                 var left = parseInt(this.getStyle("left"), 10) || 0;
9838                 var top = parseInt(this.getStyle("top"), 10) || 0;
9839                 xy = [left, top];
9840             }
9841             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9842             if(!contentBox){
9843                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9844             }else{
9845                 var l = this.getBorderWidth("l")+this.getPadding("l");
9846                 var r = this.getBorderWidth("r")+this.getPadding("r");
9847                 var t = this.getBorderWidth("t")+this.getPadding("t");
9848                 var b = this.getBorderWidth("b")+this.getPadding("b");
9849                 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)};
9850             }
9851             bx.right = bx.x + bx.width;
9852             bx.bottom = bx.y + bx.height;
9853             return bx;
9854         },
9855
9856         /**
9857          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9858          for more information about the sides.
9859          * @param {String} sides
9860          * @return {Number}
9861          */
9862         getFrameWidth : function(sides, onlyContentBox){
9863             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9864         },
9865
9866         /**
9867          * 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.
9868          * @param {Object} box The box to fill {x, y, width, height}
9869          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9870          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9871          * @return {Roo.Element} this
9872          */
9873         setBox : function(box, adjust, animate){
9874             var w = box.width, h = box.height;
9875             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9876                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9877                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9878             }
9879             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9880             return this;
9881         },
9882
9883         /**
9884          * Forces the browser to repaint this element
9885          * @return {Roo.Element} this
9886          */
9887          repaint : function(){
9888             var dom = this.dom;
9889             this.addClass("x-repaint");
9890             setTimeout(function(){
9891                 Roo.get(dom).removeClass("x-repaint");
9892             }, 1);
9893             return this;
9894         },
9895
9896         /**
9897          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9898          * then it returns the calculated width of the sides (see getPadding)
9899          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9900          * @return {Object/Number}
9901          */
9902         getMargins : function(side){
9903             if(!side){
9904                 return {
9905                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9906                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9907                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9908                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9909                 };
9910             }else{
9911                 return this.addStyles(side, El.margins);
9912              }
9913         },
9914
9915         // private
9916         addStyles : function(sides, styles){
9917             var val = 0, v, w;
9918             for(var i = 0, len = sides.length; i < len; i++){
9919                 v = this.getStyle(styles[sides.charAt(i)]);
9920                 if(v){
9921                      w = parseInt(v, 10);
9922                      if(w){ val += w; }
9923                 }
9924             }
9925             return val;
9926         },
9927
9928         /**
9929          * Creates a proxy element of this element
9930          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9931          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9932          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9933          * @return {Roo.Element} The new proxy element
9934          */
9935         createProxy : function(config, renderTo, matchBox){
9936             if(renderTo){
9937                 renderTo = Roo.getDom(renderTo);
9938             }else{
9939                 renderTo = document.body;
9940             }
9941             config = typeof config == "object" ?
9942                 config : {tag : "div", cls: config};
9943             var proxy = Roo.DomHelper.append(renderTo, config, true);
9944             if(matchBox){
9945                proxy.setBox(this.getBox());
9946             }
9947             return proxy;
9948         },
9949
9950         /**
9951          * Puts a mask over this element to disable user interaction. Requires core.css.
9952          * This method can only be applied to elements which accept child nodes.
9953          * @param {String} msg (optional) A message to display in the mask
9954          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
9955          * @return {Element} The mask  element
9956          */
9957         mask : function(msg, msgCls)
9958         {
9959             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9960                 this.setStyle("position", "relative");
9961             }
9962             if(!this._mask){
9963                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9964             }
9965             
9966             this.addClass("x-masked");
9967             this._mask.setDisplayed(true);
9968             
9969             // we wander
9970             var z = 0;
9971             var dom = this.dom;
9972             while (dom && dom.style) {
9973                 if (!isNaN(parseInt(dom.style.zIndex))) {
9974                     z = Math.max(z, parseInt(dom.style.zIndex));
9975                 }
9976                 dom = dom.parentNode;
9977             }
9978             // if we are masking the body - then it hides everything..
9979             if (this.dom == document.body) {
9980                 z = 1000000;
9981                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9982                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9983             }
9984            
9985             if(typeof msg == 'string'){
9986                 if(!this._maskMsg){
9987                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9988                         cls: "roo-el-mask-msg", 
9989                         cn: [
9990                             {
9991                                 tag: 'i',
9992                                 cls: 'fa fa-spinner fa-spin'
9993                             },
9994                             {
9995                                 tag: 'div'
9996                             }   
9997                         ]
9998                     }, true);
9999                 }
10000                 var mm = this._maskMsg;
10001                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10002                 if (mm.dom.lastChild) { // weird IE issue?
10003                     mm.dom.lastChild.innerHTML = msg;
10004                 }
10005                 mm.setDisplayed(true);
10006                 mm.center(this);
10007                 mm.setStyle('z-index', z + 102);
10008             }
10009             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10010                 this._mask.setHeight(this.getHeight());
10011             }
10012             this._mask.setStyle('z-index', z + 100);
10013             
10014             return this._mask;
10015         },
10016
10017         /**
10018          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10019          * it is cached for reuse.
10020          */
10021         unmask : function(removeEl){
10022             if(this._mask){
10023                 if(removeEl === true){
10024                     this._mask.remove();
10025                     delete this._mask;
10026                     if(this._maskMsg){
10027                         this._maskMsg.remove();
10028                         delete this._maskMsg;
10029                     }
10030                 }else{
10031                     this._mask.setDisplayed(false);
10032                     if(this._maskMsg){
10033                         this._maskMsg.setDisplayed(false);
10034                     }
10035                 }
10036             }
10037             this.removeClass("x-masked");
10038         },
10039
10040         /**
10041          * Returns true if this element is masked
10042          * @return {Boolean}
10043          */
10044         isMasked : function(){
10045             return this._mask && this._mask.isVisible();
10046         },
10047
10048         /**
10049          * Creates an iframe shim for this element to keep selects and other windowed objects from
10050          * showing through.
10051          * @return {Roo.Element} The new shim element
10052          */
10053         createShim : function(){
10054             var el = document.createElement('iframe');
10055             el.frameBorder = 'no';
10056             el.className = 'roo-shim';
10057             if(Roo.isIE && Roo.isSecure){
10058                 el.src = Roo.SSL_SECURE_URL;
10059             }
10060             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10061             shim.autoBoxAdjust = false;
10062             return shim;
10063         },
10064
10065         /**
10066          * Removes this element from the DOM and deletes it from the cache
10067          */
10068         remove : function(){
10069             if(this.dom.parentNode){
10070                 this.dom.parentNode.removeChild(this.dom);
10071             }
10072             delete El.cache[this.dom.id];
10073         },
10074
10075         /**
10076          * Sets up event handlers to add and remove a css class when the mouse is over this element
10077          * @param {String} className
10078          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10079          * mouseout events for children elements
10080          * @return {Roo.Element} this
10081          */
10082         addClassOnOver : function(className, preventFlicker){
10083             this.on("mouseover", function(){
10084                 Roo.fly(this, '_internal').addClass(className);
10085             }, this.dom);
10086             var removeFn = function(e){
10087                 if(preventFlicker !== true || !e.within(this, true)){
10088                     Roo.fly(this, '_internal').removeClass(className);
10089                 }
10090             };
10091             this.on("mouseout", removeFn, this.dom);
10092             return this;
10093         },
10094
10095         /**
10096          * Sets up event handlers to add and remove a css class when this element has the focus
10097          * @param {String} className
10098          * @return {Roo.Element} this
10099          */
10100         addClassOnFocus : function(className){
10101             this.on("focus", function(){
10102                 Roo.fly(this, '_internal').addClass(className);
10103             }, this.dom);
10104             this.on("blur", function(){
10105                 Roo.fly(this, '_internal').removeClass(className);
10106             }, this.dom);
10107             return this;
10108         },
10109         /**
10110          * 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)
10111          * @param {String} className
10112          * @return {Roo.Element} this
10113          */
10114         addClassOnClick : function(className){
10115             var dom = this.dom;
10116             this.on("mousedown", function(){
10117                 Roo.fly(dom, '_internal').addClass(className);
10118                 var d = Roo.get(document);
10119                 var fn = function(){
10120                     Roo.fly(dom, '_internal').removeClass(className);
10121                     d.removeListener("mouseup", fn);
10122                 };
10123                 d.on("mouseup", fn);
10124             });
10125             return this;
10126         },
10127
10128         /**
10129          * Stops the specified event from bubbling and optionally prevents the default action
10130          * @param {String} eventName
10131          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10132          * @return {Roo.Element} this
10133          */
10134         swallowEvent : function(eventName, preventDefault){
10135             var fn = function(e){
10136                 e.stopPropagation();
10137                 if(preventDefault){
10138                     e.preventDefault();
10139                 }
10140             };
10141             if(eventName instanceof Array){
10142                 for(var i = 0, len = eventName.length; i < len; i++){
10143                      this.on(eventName[i], fn);
10144                 }
10145                 return this;
10146             }
10147             this.on(eventName, fn);
10148             return this;
10149         },
10150
10151         /**
10152          * @private
10153          */
10154         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10155
10156         /**
10157          * Sizes this element to its parent element's dimensions performing
10158          * neccessary box adjustments.
10159          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10160          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10161          * @return {Roo.Element} this
10162          */
10163         fitToParent : function(monitorResize, targetParent) {
10164           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10165           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10166           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10167             return this;
10168           }
10169           var p = Roo.get(targetParent || this.dom.parentNode);
10170           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10171           if (monitorResize === true) {
10172             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10173             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10174           }
10175           return this;
10176         },
10177
10178         /**
10179          * Gets the next sibling, skipping text nodes
10180          * @return {HTMLElement} The next sibling or null
10181          */
10182         getNextSibling : function(){
10183             var n = this.dom.nextSibling;
10184             while(n && n.nodeType != 1){
10185                 n = n.nextSibling;
10186             }
10187             return n;
10188         },
10189
10190         /**
10191          * Gets the previous sibling, skipping text nodes
10192          * @return {HTMLElement} The previous sibling or null
10193          */
10194         getPrevSibling : function(){
10195             var n = this.dom.previousSibling;
10196             while(n && n.nodeType != 1){
10197                 n = n.previousSibling;
10198             }
10199             return n;
10200         },
10201
10202
10203         /**
10204          * Appends the passed element(s) to this element
10205          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10206          * @return {Roo.Element} this
10207          */
10208         appendChild: function(el){
10209             el = Roo.get(el);
10210             el.appendTo(this);
10211             return this;
10212         },
10213
10214         /**
10215          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10216          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10217          * automatically generated with the specified attributes.
10218          * @param {HTMLElement} insertBefore (optional) a child element of this element
10219          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10220          * @return {Roo.Element} The new child element
10221          */
10222         createChild: function(config, insertBefore, returnDom){
10223             config = config || {tag:'div'};
10224             if(insertBefore){
10225                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10226             }
10227             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10228         },
10229
10230         /**
10231          * Appends this element to the passed element
10232          * @param {String/HTMLElement/Element} el The new parent element
10233          * @return {Roo.Element} this
10234          */
10235         appendTo: function(el){
10236             el = Roo.getDom(el);
10237             el.appendChild(this.dom);
10238             return this;
10239         },
10240
10241         /**
10242          * Inserts this element before the passed element in the DOM
10243          * @param {String/HTMLElement/Element} el The element to insert before
10244          * @return {Roo.Element} this
10245          */
10246         insertBefore: function(el){
10247             el = Roo.getDom(el);
10248             el.parentNode.insertBefore(this.dom, el);
10249             return this;
10250         },
10251
10252         /**
10253          * Inserts this element after the passed element in the DOM
10254          * @param {String/HTMLElement/Element} el The element to insert after
10255          * @return {Roo.Element} this
10256          */
10257         insertAfter: function(el){
10258             el = Roo.getDom(el);
10259             el.parentNode.insertBefore(this.dom, el.nextSibling);
10260             return this;
10261         },
10262
10263         /**
10264          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10265          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10266          * @return {Roo.Element} The new child
10267          */
10268         insertFirst: function(el, returnDom){
10269             el = el || {};
10270             if(typeof el == 'object' && !el.nodeType){ // dh config
10271                 return this.createChild(el, this.dom.firstChild, returnDom);
10272             }else{
10273                 el = Roo.getDom(el);
10274                 this.dom.insertBefore(el, this.dom.firstChild);
10275                 return !returnDom ? Roo.get(el) : el;
10276             }
10277         },
10278
10279         /**
10280          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10281          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10282          * @param {String} where (optional) 'before' or 'after' defaults to before
10283          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10284          * @return {Roo.Element} the inserted Element
10285          */
10286         insertSibling: function(el, where, returnDom){
10287             where = where ? where.toLowerCase() : 'before';
10288             el = el || {};
10289             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10290
10291             if(typeof el == 'object' && !el.nodeType){ // dh config
10292                 if(where == 'after' && !this.dom.nextSibling){
10293                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10294                 }else{
10295                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10296                 }
10297
10298             }else{
10299                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10300                             where == 'before' ? this.dom : this.dom.nextSibling);
10301                 if(!returnDom){
10302                     rt = Roo.get(rt);
10303                 }
10304             }
10305             return rt;
10306         },
10307
10308         /**
10309          * Creates and wraps this element with another element
10310          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10311          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10312          * @return {HTMLElement/Element} The newly created wrapper element
10313          */
10314         wrap: function(config, returnDom){
10315             if(!config){
10316                 config = {tag: "div"};
10317             }
10318             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10319             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10320             return newEl;
10321         },
10322
10323         /**
10324          * Replaces the passed element with this element
10325          * @param {String/HTMLElement/Element} el The element to replace
10326          * @return {Roo.Element} this
10327          */
10328         replace: function(el){
10329             el = Roo.get(el);
10330             this.insertBefore(el);
10331             el.remove();
10332             return this;
10333         },
10334
10335         /**
10336          * Inserts an html fragment into this element
10337          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10338          * @param {String} html The HTML fragment
10339          * @param {Boolean} returnEl True to return an Roo.Element
10340          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10341          */
10342         insertHtml : function(where, html, returnEl){
10343             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10344             return returnEl ? Roo.get(el) : el;
10345         },
10346
10347         /**
10348          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10349          * @param {Object} o The object with the attributes
10350          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10351          * @return {Roo.Element} this
10352          */
10353         set : function(o, useSet){
10354             var el = this.dom;
10355             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10356             for(var attr in o){
10357                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10358                 if(attr=="cls"){
10359                     el.className = o["cls"];
10360                 }else{
10361                     if(useSet) {
10362                         el.setAttribute(attr, o[attr]);
10363                     } else {
10364                         el[attr] = o[attr];
10365                     }
10366                 }
10367             }
10368             if(o.style){
10369                 Roo.DomHelper.applyStyles(el, o.style);
10370             }
10371             return this;
10372         },
10373
10374         /**
10375          * Convenience method for constructing a KeyMap
10376          * @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:
10377          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10378          * @param {Function} fn The function to call
10379          * @param {Object} scope (optional) The scope of the function
10380          * @return {Roo.KeyMap} The KeyMap created
10381          */
10382         addKeyListener : function(key, fn, scope){
10383             var config;
10384             if(typeof key != "object" || key instanceof Array){
10385                 config = {
10386                     key: key,
10387                     fn: fn,
10388                     scope: scope
10389                 };
10390             }else{
10391                 config = {
10392                     key : key.key,
10393                     shift : key.shift,
10394                     ctrl : key.ctrl,
10395                     alt : key.alt,
10396                     fn: fn,
10397                     scope: scope
10398                 };
10399             }
10400             return new Roo.KeyMap(this, config);
10401         },
10402
10403         /**
10404          * Creates a KeyMap for this element
10405          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10406          * @return {Roo.KeyMap} The KeyMap created
10407          */
10408         addKeyMap : function(config){
10409             return new Roo.KeyMap(this, config);
10410         },
10411
10412         /**
10413          * Returns true if this element is scrollable.
10414          * @return {Boolean}
10415          */
10416          isScrollable : function(){
10417             var dom = this.dom;
10418             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10419         },
10420
10421         /**
10422          * 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().
10423          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10424          * @param {Number} value The new scroll value
10425          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10426          * @return {Element} this
10427          */
10428
10429         scrollTo : function(side, value, animate){
10430             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10431             if(!animate || !A){
10432                 this.dom[prop] = value;
10433             }else{
10434                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10435                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10436             }
10437             return this;
10438         },
10439
10440         /**
10441          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10442          * within this element's scrollable range.
10443          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10444          * @param {Number} distance How far to scroll the element in pixels
10445          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10446          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10447          * was scrolled as far as it could go.
10448          */
10449          scroll : function(direction, distance, animate){
10450              if(!this.isScrollable()){
10451                  return;
10452              }
10453              var el = this.dom;
10454              var l = el.scrollLeft, t = el.scrollTop;
10455              var w = el.scrollWidth, h = el.scrollHeight;
10456              var cw = el.clientWidth, ch = el.clientHeight;
10457              direction = direction.toLowerCase();
10458              var scrolled = false;
10459              var a = this.preanim(arguments, 2);
10460              switch(direction){
10461                  case "l":
10462                  case "left":
10463                      if(w - l > cw){
10464                          var v = Math.min(l + distance, w-cw);
10465                          this.scrollTo("left", v, a);
10466                          scrolled = true;
10467                      }
10468                      break;
10469                 case "r":
10470                 case "right":
10471                      if(l > 0){
10472                          var v = Math.max(l - distance, 0);
10473                          this.scrollTo("left", v, a);
10474                          scrolled = true;
10475                      }
10476                      break;
10477                 case "t":
10478                 case "top":
10479                 case "up":
10480                      if(t > 0){
10481                          var v = Math.max(t - distance, 0);
10482                          this.scrollTo("top", v, a);
10483                          scrolled = true;
10484                      }
10485                      break;
10486                 case "b":
10487                 case "bottom":
10488                 case "down":
10489                      if(h - t > ch){
10490                          var v = Math.min(t + distance, h-ch);
10491                          this.scrollTo("top", v, a);
10492                          scrolled = true;
10493                      }
10494                      break;
10495              }
10496              return scrolled;
10497         },
10498
10499         /**
10500          * Translates the passed page coordinates into left/top css values for this element
10501          * @param {Number/Array} x The page x or an array containing [x, y]
10502          * @param {Number} y The page y
10503          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10504          */
10505         translatePoints : function(x, y){
10506             if(typeof x == 'object' || x instanceof Array){
10507                 y = x[1]; x = x[0];
10508             }
10509             var p = this.getStyle('position');
10510             var o = this.getXY();
10511
10512             var l = parseInt(this.getStyle('left'), 10);
10513             var t = parseInt(this.getStyle('top'), 10);
10514
10515             if(isNaN(l)){
10516                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10517             }
10518             if(isNaN(t)){
10519                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10520             }
10521
10522             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10523         },
10524
10525         /**
10526          * Returns the current scroll position of the element.
10527          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10528          */
10529         getScroll : function(){
10530             var d = this.dom, doc = document;
10531             if(d == doc || d == doc.body){
10532                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10533                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10534                 return {left: l, top: t};
10535             }else{
10536                 return {left: d.scrollLeft, top: d.scrollTop};
10537             }
10538         },
10539
10540         /**
10541          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10542          * are convert to standard 6 digit hex color.
10543          * @param {String} attr The css attribute
10544          * @param {String} defaultValue The default value to use when a valid color isn't found
10545          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10546          * YUI color anims.
10547          */
10548         getColor : function(attr, defaultValue, prefix){
10549             var v = this.getStyle(attr);
10550             if(!v || v == "transparent" || v == "inherit") {
10551                 return defaultValue;
10552             }
10553             var color = typeof prefix == "undefined" ? "#" : prefix;
10554             if(v.substr(0, 4) == "rgb("){
10555                 var rvs = v.slice(4, v.length -1).split(",");
10556                 for(var i = 0; i < 3; i++){
10557                     var h = parseInt(rvs[i]).toString(16);
10558                     if(h < 16){
10559                         h = "0" + h;
10560                     }
10561                     color += h;
10562                 }
10563             } else {
10564                 if(v.substr(0, 1) == "#"){
10565                     if(v.length == 4) {
10566                         for(var i = 1; i < 4; i++){
10567                             var c = v.charAt(i);
10568                             color +=  c + c;
10569                         }
10570                     }else if(v.length == 7){
10571                         color += v.substr(1);
10572                     }
10573                 }
10574             }
10575             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10576         },
10577
10578         /**
10579          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10580          * gradient background, rounded corners and a 4-way shadow.
10581          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10582          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10583          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10584          * @return {Roo.Element} this
10585          */
10586         boxWrap : function(cls){
10587             cls = cls || 'x-box';
10588             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10589             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10590             return el;
10591         },
10592
10593         /**
10594          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10595          * @param {String} namespace The namespace in which to look for the attribute
10596          * @param {String} name The attribute name
10597          * @return {String} The attribute value
10598          */
10599         getAttributeNS : Roo.isIE ? function(ns, name){
10600             var d = this.dom;
10601             var type = typeof d[ns+":"+name];
10602             if(type != 'undefined' && type != 'unknown'){
10603                 return d[ns+":"+name];
10604             }
10605             return d[name];
10606         } : function(ns, name){
10607             var d = this.dom;
10608             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10609         },
10610         
10611         
10612         /**
10613          * Sets or Returns the value the dom attribute value
10614          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10615          * @param {String} value (optional) The value to set the attribute to
10616          * @return {String} The attribute value
10617          */
10618         attr : function(name){
10619             if (arguments.length > 1) {
10620                 this.dom.setAttribute(name, arguments[1]);
10621                 return arguments[1];
10622             }
10623             if (typeof(name) == 'object') {
10624                 for(var i in name) {
10625                     this.attr(i, name[i]);
10626                 }
10627                 return name;
10628             }
10629             
10630             
10631             if (!this.dom.hasAttribute(name)) {
10632                 return undefined;
10633             }
10634             return this.dom.getAttribute(name);
10635         }
10636         
10637         
10638         
10639     };
10640
10641     var ep = El.prototype;
10642
10643     /**
10644      * Appends an event handler (Shorthand for addListener)
10645      * @param {String}   eventName     The type of event to append
10646      * @param {Function} fn        The method the event invokes
10647      * @param {Object} scope       (optional) The scope (this object) of the fn
10648      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10649      * @method
10650      */
10651     ep.on = ep.addListener;
10652         // backwards compat
10653     ep.mon = ep.addListener;
10654
10655     /**
10656      * Removes an event handler from this element (shorthand for removeListener)
10657      * @param {String} eventName the type of event to remove
10658      * @param {Function} fn the method the event invokes
10659      * @return {Roo.Element} this
10660      * @method
10661      */
10662     ep.un = ep.removeListener;
10663
10664     /**
10665      * true to automatically adjust width and height settings for box-model issues (default to true)
10666      */
10667     ep.autoBoxAdjust = true;
10668
10669     // private
10670     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10671
10672     // private
10673     El.addUnits = function(v, defaultUnit){
10674         if(v === "" || v == "auto"){
10675             return v;
10676         }
10677         if(v === undefined){
10678             return '';
10679         }
10680         if(typeof v == "number" || !El.unitPattern.test(v)){
10681             return v + (defaultUnit || 'px');
10682         }
10683         return v;
10684     };
10685
10686     // special markup used throughout Roo when box wrapping elements
10687     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>';
10688     /**
10689      * Visibility mode constant - Use visibility to hide element
10690      * @static
10691      * @type Number
10692      */
10693     El.VISIBILITY = 1;
10694     /**
10695      * Visibility mode constant - Use display to hide element
10696      * @static
10697      * @type Number
10698      */
10699     El.DISPLAY = 2;
10700
10701     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10702     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10703     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10704
10705
10706
10707     /**
10708      * @private
10709      */
10710     El.cache = {};
10711
10712     var docEl;
10713
10714     /**
10715      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10716      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10717      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10718      * @return {Element} The Element object
10719      * @static
10720      */
10721     El.get = function(el){
10722         var ex, elm, id;
10723         if(!el){ return null; }
10724         if(typeof el == "string"){ // element id
10725             if(!(elm = document.getElementById(el))){
10726                 return null;
10727             }
10728             if(ex = El.cache[el]){
10729                 ex.dom = elm;
10730             }else{
10731                 ex = El.cache[el] = new El(elm);
10732             }
10733             return ex;
10734         }else if(el.tagName){ // dom element
10735             if(!(id = el.id)){
10736                 id = Roo.id(el);
10737             }
10738             if(ex = El.cache[id]){
10739                 ex.dom = el;
10740             }else{
10741                 ex = El.cache[id] = new El(el);
10742             }
10743             return ex;
10744         }else if(el instanceof El){
10745             if(el != docEl){
10746                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10747                                                               // catch case where it hasn't been appended
10748                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10749             }
10750             return el;
10751         }else if(el.isComposite){
10752             return el;
10753         }else if(el instanceof Array){
10754             return El.select(el);
10755         }else if(el == document){
10756             // create a bogus element object representing the document object
10757             if(!docEl){
10758                 var f = function(){};
10759                 f.prototype = El.prototype;
10760                 docEl = new f();
10761                 docEl.dom = document;
10762             }
10763             return docEl;
10764         }
10765         return null;
10766     };
10767
10768     // private
10769     El.uncache = function(el){
10770         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10771             if(a[i]){
10772                 delete El.cache[a[i].id || a[i]];
10773             }
10774         }
10775     };
10776
10777     // private
10778     // Garbage collection - uncache elements/purge listeners on orphaned elements
10779     // so we don't hold a reference and cause the browser to retain them
10780     El.garbageCollect = function(){
10781         if(!Roo.enableGarbageCollector){
10782             clearInterval(El.collectorThread);
10783             return;
10784         }
10785         for(var eid in El.cache){
10786             var el = El.cache[eid], d = el.dom;
10787             // -------------------------------------------------------
10788             // Determining what is garbage:
10789             // -------------------------------------------------------
10790             // !d
10791             // dom node is null, definitely garbage
10792             // -------------------------------------------------------
10793             // !d.parentNode
10794             // no parentNode == direct orphan, definitely garbage
10795             // -------------------------------------------------------
10796             // !d.offsetParent && !document.getElementById(eid)
10797             // display none elements have no offsetParent so we will
10798             // also try to look it up by it's id. However, check
10799             // offsetParent first so we don't do unneeded lookups.
10800             // This enables collection of elements that are not orphans
10801             // directly, but somewhere up the line they have an orphan
10802             // parent.
10803             // -------------------------------------------------------
10804             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10805                 delete El.cache[eid];
10806                 if(d && Roo.enableListenerCollection){
10807                     E.purgeElement(d);
10808                 }
10809             }
10810         }
10811     }
10812     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10813
10814
10815     // dom is optional
10816     El.Flyweight = function(dom){
10817         this.dom = dom;
10818     };
10819     El.Flyweight.prototype = El.prototype;
10820
10821     El._flyweights = {};
10822     /**
10823      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10824      * the dom node can be overwritten by other code.
10825      * @param {String/HTMLElement} el The dom node or id
10826      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10827      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10828      * @static
10829      * @return {Element} The shared Element object
10830      */
10831     El.fly = function(el, named){
10832         named = named || '_global';
10833         el = Roo.getDom(el);
10834         if(!el){
10835             return null;
10836         }
10837         if(!El._flyweights[named]){
10838             El._flyweights[named] = new El.Flyweight();
10839         }
10840         El._flyweights[named].dom = el;
10841         return El._flyweights[named];
10842     };
10843
10844     /**
10845      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10846      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10847      * Shorthand of {@link Roo.Element#get}
10848      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10849      * @return {Element} The Element object
10850      * @member Roo
10851      * @method get
10852      */
10853     Roo.get = El.get;
10854     /**
10855      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10856      * the dom node can be overwritten by other code.
10857      * Shorthand of {@link Roo.Element#fly}
10858      * @param {String/HTMLElement} el The dom node or id
10859      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10860      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10861      * @static
10862      * @return {Element} The shared Element object
10863      * @member Roo
10864      * @method fly
10865      */
10866     Roo.fly = El.fly;
10867
10868     // speedy lookup for elements never to box adjust
10869     var noBoxAdjust = Roo.isStrict ? {
10870         select:1
10871     } : {
10872         input:1, select:1, textarea:1
10873     };
10874     if(Roo.isIE || Roo.isGecko){
10875         noBoxAdjust['button'] = 1;
10876     }
10877
10878
10879     Roo.EventManager.on(window, 'unload', function(){
10880         delete El.cache;
10881         delete El._flyweights;
10882     });
10883 })();
10884
10885
10886
10887
10888 if(Roo.DomQuery){
10889     Roo.Element.selectorFunction = Roo.DomQuery.select;
10890 }
10891
10892 Roo.Element.select = function(selector, unique, root){
10893     var els;
10894     if(typeof selector == "string"){
10895         els = Roo.Element.selectorFunction(selector, root);
10896     }else if(selector.length !== undefined){
10897         els = selector;
10898     }else{
10899         throw "Invalid selector";
10900     }
10901     if(unique === true){
10902         return new Roo.CompositeElement(els);
10903     }else{
10904         return new Roo.CompositeElementLite(els);
10905     }
10906 };
10907 /**
10908  * Selects elements based on the passed CSS selector to enable working on them as 1.
10909  * @param {String/Array} selector The CSS selector or an array of elements
10910  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10911  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10912  * @return {CompositeElementLite/CompositeElement}
10913  * @member Roo
10914  * @method select
10915  */
10916 Roo.select = Roo.Element.select;
10917
10918
10919
10920
10921
10922
10923
10924
10925
10926
10927
10928
10929
10930
10931 /*
10932  * Based on:
10933  * Ext JS Library 1.1.1
10934  * Copyright(c) 2006-2007, Ext JS, LLC.
10935  *
10936  * Originally Released Under LGPL - original licence link has changed is not relivant.
10937  *
10938  * Fork - LGPL
10939  * <script type="text/javascript">
10940  */
10941
10942
10943
10944 //Notifies Element that fx methods are available
10945 Roo.enableFx = true;
10946
10947 /**
10948  * @class Roo.Fx
10949  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10950  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10951  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10952  * Element effects to work.</p><br/>
10953  *
10954  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10955  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10956  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10957  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10958  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10959  * expected results and should be done with care.</p><br/>
10960  *
10961  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10962  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10963 <pre>
10964 Value  Description
10965 -----  -----------------------------
10966 tl     The top left corner
10967 t      The center of the top edge
10968 tr     The top right corner
10969 l      The center of the left edge
10970 r      The center of the right edge
10971 bl     The bottom left corner
10972 b      The center of the bottom edge
10973 br     The bottom right corner
10974 </pre>
10975  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10976  * below are common options that can be passed to any Fx method.</b>
10977  * @cfg {Function} callback A function called when the effect is finished
10978  * @cfg {Object} scope The scope of the effect function
10979  * @cfg {String} easing A valid Easing value for the effect
10980  * @cfg {String} afterCls A css class to apply after the effect
10981  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10982  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10983  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10984  * effects that end with the element being visually hidden, ignored otherwise)
10985  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10986  * a function which returns such a specification that will be applied to the Element after the effect finishes
10987  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10988  * @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
10989  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10990  */
10991 Roo.Fx = {
10992         /**
10993          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10994          * origin for the slide effect.  This function automatically handles wrapping the element with
10995          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10996          * Usage:
10997          *<pre><code>
10998 // default: slide the element in from the top
10999 el.slideIn();
11000
11001 // custom: slide the element in from the right with a 2-second duration
11002 el.slideIn('r', { duration: 2 });
11003
11004 // common config options shown with default values
11005 el.slideIn('t', {
11006     easing: 'easeOut',
11007     duration: .5
11008 });
11009 </code></pre>
11010          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11011          * @param {Object} options (optional) Object literal with any of the Fx config options
11012          * @return {Roo.Element} The Element
11013          */
11014     slideIn : function(anchor, o){
11015         var el = this.getFxEl();
11016         o = o || {};
11017
11018         el.queueFx(o, function(){
11019
11020             anchor = anchor || "t";
11021
11022             // fix display to visibility
11023             this.fixDisplay();
11024
11025             // restore values after effect
11026             var r = this.getFxRestore();
11027             var b = this.getBox();
11028             // fixed size for slide
11029             this.setSize(b);
11030
11031             // wrap if needed
11032             var wrap = this.fxWrap(r.pos, o, "hidden");
11033
11034             var st = this.dom.style;
11035             st.visibility = "visible";
11036             st.position = "absolute";
11037
11038             // clear out temp styles after slide and unwrap
11039             var after = function(){
11040                 el.fxUnwrap(wrap, r.pos, o);
11041                 st.width = r.width;
11042                 st.height = r.height;
11043                 el.afterFx(o);
11044             };
11045             // time to calc the positions
11046             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11047
11048             switch(anchor.toLowerCase()){
11049                 case "t":
11050                     wrap.setSize(b.width, 0);
11051                     st.left = st.bottom = "0";
11052                     a = {height: bh};
11053                 break;
11054                 case "l":
11055                     wrap.setSize(0, b.height);
11056                     st.right = st.top = "0";
11057                     a = {width: bw};
11058                 break;
11059                 case "r":
11060                     wrap.setSize(0, b.height);
11061                     wrap.setX(b.right);
11062                     st.left = st.top = "0";
11063                     a = {width: bw, points: pt};
11064                 break;
11065                 case "b":
11066                     wrap.setSize(b.width, 0);
11067                     wrap.setY(b.bottom);
11068                     st.left = st.top = "0";
11069                     a = {height: bh, points: pt};
11070                 break;
11071                 case "tl":
11072                     wrap.setSize(0, 0);
11073                     st.right = st.bottom = "0";
11074                     a = {width: bw, height: bh};
11075                 break;
11076                 case "bl":
11077                     wrap.setSize(0, 0);
11078                     wrap.setY(b.y+b.height);
11079                     st.right = st.top = "0";
11080                     a = {width: bw, height: bh, points: pt};
11081                 break;
11082                 case "br":
11083                     wrap.setSize(0, 0);
11084                     wrap.setXY([b.right, b.bottom]);
11085                     st.left = st.top = "0";
11086                     a = {width: bw, height: bh, points: pt};
11087                 break;
11088                 case "tr":
11089                     wrap.setSize(0, 0);
11090                     wrap.setX(b.x+b.width);
11091                     st.left = st.bottom = "0";
11092                     a = {width: bw, height: bh, points: pt};
11093                 break;
11094             }
11095             this.dom.style.visibility = "visible";
11096             wrap.show();
11097
11098             arguments.callee.anim = wrap.fxanim(a,
11099                 o,
11100                 'motion',
11101                 .5,
11102                 'easeOut', after);
11103         });
11104         return this;
11105     },
11106     
11107         /**
11108          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11109          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11110          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11111          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11112          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11113          * Usage:
11114          *<pre><code>
11115 // default: slide the element out to the top
11116 el.slideOut();
11117
11118 // custom: slide the element out to the right with a 2-second duration
11119 el.slideOut('r', { duration: 2 });
11120
11121 // common config options shown with default values
11122 el.slideOut('t', {
11123     easing: 'easeOut',
11124     duration: .5,
11125     remove: false,
11126     useDisplay: false
11127 });
11128 </code></pre>
11129          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11130          * @param {Object} options (optional) Object literal with any of the Fx config options
11131          * @return {Roo.Element} The Element
11132          */
11133     slideOut : function(anchor, o){
11134         var el = this.getFxEl();
11135         o = o || {};
11136
11137         el.queueFx(o, function(){
11138
11139             anchor = anchor || "t";
11140
11141             // restore values after effect
11142             var r = this.getFxRestore();
11143             
11144             var b = this.getBox();
11145             // fixed size for slide
11146             this.setSize(b);
11147
11148             // wrap if needed
11149             var wrap = this.fxWrap(r.pos, o, "visible");
11150
11151             var st = this.dom.style;
11152             st.visibility = "visible";
11153             st.position = "absolute";
11154
11155             wrap.setSize(b);
11156
11157             var after = function(){
11158                 if(o.useDisplay){
11159                     el.setDisplayed(false);
11160                 }else{
11161                     el.hide();
11162                 }
11163
11164                 el.fxUnwrap(wrap, r.pos, o);
11165
11166                 st.width = r.width;
11167                 st.height = r.height;
11168
11169                 el.afterFx(o);
11170             };
11171
11172             var a, zero = {to: 0};
11173             switch(anchor.toLowerCase()){
11174                 case "t":
11175                     st.left = st.bottom = "0";
11176                     a = {height: zero};
11177                 break;
11178                 case "l":
11179                     st.right = st.top = "0";
11180                     a = {width: zero};
11181                 break;
11182                 case "r":
11183                     st.left = st.top = "0";
11184                     a = {width: zero, points: {to:[b.right, b.y]}};
11185                 break;
11186                 case "b":
11187                     st.left = st.top = "0";
11188                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11189                 break;
11190                 case "tl":
11191                     st.right = st.bottom = "0";
11192                     a = {width: zero, height: zero};
11193                 break;
11194                 case "bl":
11195                     st.right = st.top = "0";
11196                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11197                 break;
11198                 case "br":
11199                     st.left = st.top = "0";
11200                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11201                 break;
11202                 case "tr":
11203                     st.left = st.bottom = "0";
11204                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11205                 break;
11206             }
11207
11208             arguments.callee.anim = wrap.fxanim(a,
11209                 o,
11210                 'motion',
11211                 .5,
11212                 "easeOut", after);
11213         });
11214         return this;
11215     },
11216
11217         /**
11218          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11219          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11220          * The element must be removed from the DOM using the 'remove' config option if desired.
11221          * Usage:
11222          *<pre><code>
11223 // default
11224 el.puff();
11225
11226 // common config options shown with default values
11227 el.puff({
11228     easing: 'easeOut',
11229     duration: .5,
11230     remove: false,
11231     useDisplay: false
11232 });
11233 </code></pre>
11234          * @param {Object} options (optional) Object literal with any of the Fx config options
11235          * @return {Roo.Element} The Element
11236          */
11237     puff : function(o){
11238         var el = this.getFxEl();
11239         o = o || {};
11240
11241         el.queueFx(o, function(){
11242             this.clearOpacity();
11243             this.show();
11244
11245             // restore values after effect
11246             var r = this.getFxRestore();
11247             var st = this.dom.style;
11248
11249             var after = function(){
11250                 if(o.useDisplay){
11251                     el.setDisplayed(false);
11252                 }else{
11253                     el.hide();
11254                 }
11255
11256                 el.clearOpacity();
11257
11258                 el.setPositioning(r.pos);
11259                 st.width = r.width;
11260                 st.height = r.height;
11261                 st.fontSize = '';
11262                 el.afterFx(o);
11263             };
11264
11265             var width = this.getWidth();
11266             var height = this.getHeight();
11267
11268             arguments.callee.anim = this.fxanim({
11269                     width : {to: this.adjustWidth(width * 2)},
11270                     height : {to: this.adjustHeight(height * 2)},
11271                     points : {by: [-(width * .5), -(height * .5)]},
11272                     opacity : {to: 0},
11273                     fontSize: {to:200, unit: "%"}
11274                 },
11275                 o,
11276                 'motion',
11277                 .5,
11278                 "easeOut", after);
11279         });
11280         return this;
11281     },
11282
11283         /**
11284          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11285          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11286          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11287          * Usage:
11288          *<pre><code>
11289 // default
11290 el.switchOff();
11291
11292 // all config options shown with default values
11293 el.switchOff({
11294     easing: 'easeIn',
11295     duration: .3,
11296     remove: false,
11297     useDisplay: false
11298 });
11299 </code></pre>
11300          * @param {Object} options (optional) Object literal with any of the Fx config options
11301          * @return {Roo.Element} The Element
11302          */
11303     switchOff : function(o){
11304         var el = this.getFxEl();
11305         o = o || {};
11306
11307         el.queueFx(o, function(){
11308             this.clearOpacity();
11309             this.clip();
11310
11311             // restore values after effect
11312             var r = this.getFxRestore();
11313             var st = this.dom.style;
11314
11315             var after = function(){
11316                 if(o.useDisplay){
11317                     el.setDisplayed(false);
11318                 }else{
11319                     el.hide();
11320                 }
11321
11322                 el.clearOpacity();
11323                 el.setPositioning(r.pos);
11324                 st.width = r.width;
11325                 st.height = r.height;
11326
11327                 el.afterFx(o);
11328             };
11329
11330             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11331                 this.clearOpacity();
11332                 (function(){
11333                     this.fxanim({
11334                         height:{to:1},
11335                         points:{by:[0, this.getHeight() * .5]}
11336                     }, o, 'motion', 0.3, 'easeIn', after);
11337                 }).defer(100, this);
11338             });
11339         });
11340         return this;
11341     },
11342
11343     /**
11344      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11345      * changed using the "attr" config option) and then fading back to the original color. If no original
11346      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11347      * Usage:
11348 <pre><code>
11349 // default: highlight background to yellow
11350 el.highlight();
11351
11352 // custom: highlight foreground text to blue for 2 seconds
11353 el.highlight("0000ff", { attr: 'color', duration: 2 });
11354
11355 // common config options shown with default values
11356 el.highlight("ffff9c", {
11357     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11358     endColor: (current color) or "ffffff",
11359     easing: 'easeIn',
11360     duration: 1
11361 });
11362 </code></pre>
11363      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11364      * @param {Object} options (optional) Object literal with any of the Fx config options
11365      * @return {Roo.Element} The Element
11366      */ 
11367     highlight : function(color, o){
11368         var el = this.getFxEl();
11369         o = o || {};
11370
11371         el.queueFx(o, function(){
11372             color = color || "ffff9c";
11373             attr = o.attr || "backgroundColor";
11374
11375             this.clearOpacity();
11376             this.show();
11377
11378             var origColor = this.getColor(attr);
11379             var restoreColor = this.dom.style[attr];
11380             endColor = (o.endColor || origColor) || "ffffff";
11381
11382             var after = function(){
11383                 el.dom.style[attr] = restoreColor;
11384                 el.afterFx(o);
11385             };
11386
11387             var a = {};
11388             a[attr] = {from: color, to: endColor};
11389             arguments.callee.anim = this.fxanim(a,
11390                 o,
11391                 'color',
11392                 1,
11393                 'easeIn', after);
11394         });
11395         return this;
11396     },
11397
11398    /**
11399     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11400     * Usage:
11401 <pre><code>
11402 // default: a single light blue ripple
11403 el.frame();
11404
11405 // custom: 3 red ripples lasting 3 seconds total
11406 el.frame("ff0000", 3, { duration: 3 });
11407
11408 // common config options shown with default values
11409 el.frame("C3DAF9", 1, {
11410     duration: 1 //duration of entire animation (not each individual ripple)
11411     // Note: Easing is not configurable and will be ignored if included
11412 });
11413 </code></pre>
11414     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11415     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11416     * @param {Object} options (optional) Object literal with any of the Fx config options
11417     * @return {Roo.Element} The Element
11418     */
11419     frame : function(color, count, o){
11420         var el = this.getFxEl();
11421         o = o || {};
11422
11423         el.queueFx(o, function(){
11424             color = color || "#C3DAF9";
11425             if(color.length == 6){
11426                 color = "#" + color;
11427             }
11428             count = count || 1;
11429             duration = o.duration || 1;
11430             this.show();
11431
11432             var b = this.getBox();
11433             var animFn = function(){
11434                 var proxy = this.createProxy({
11435
11436                      style:{
11437                         visbility:"hidden",
11438                         position:"absolute",
11439                         "z-index":"35000", // yee haw
11440                         border:"0px solid " + color
11441                      }
11442                   });
11443                 var scale = Roo.isBorderBox ? 2 : 1;
11444                 proxy.animate({
11445                     top:{from:b.y, to:b.y - 20},
11446                     left:{from:b.x, to:b.x - 20},
11447                     borderWidth:{from:0, to:10},
11448                     opacity:{from:1, to:0},
11449                     height:{from:b.height, to:(b.height + (20*scale))},
11450                     width:{from:b.width, to:(b.width + (20*scale))}
11451                 }, duration, function(){
11452                     proxy.remove();
11453                 });
11454                 if(--count > 0){
11455                      animFn.defer((duration/2)*1000, this);
11456                 }else{
11457                     el.afterFx(o);
11458                 }
11459             };
11460             animFn.call(this);
11461         });
11462         return this;
11463     },
11464
11465    /**
11466     * Creates a pause before any subsequent queued effects begin.  If there are
11467     * no effects queued after the pause it will have no effect.
11468     * Usage:
11469 <pre><code>
11470 el.pause(1);
11471 </code></pre>
11472     * @param {Number} seconds The length of time to pause (in seconds)
11473     * @return {Roo.Element} The Element
11474     */
11475     pause : function(seconds){
11476         var el = this.getFxEl();
11477         var o = {};
11478
11479         el.queueFx(o, function(){
11480             setTimeout(function(){
11481                 el.afterFx(o);
11482             }, seconds * 1000);
11483         });
11484         return this;
11485     },
11486
11487    /**
11488     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11489     * using the "endOpacity" config option.
11490     * Usage:
11491 <pre><code>
11492 // default: fade in from opacity 0 to 100%
11493 el.fadeIn();
11494
11495 // custom: fade in from opacity 0 to 75% over 2 seconds
11496 el.fadeIn({ endOpacity: .75, duration: 2});
11497
11498 // common config options shown with default values
11499 el.fadeIn({
11500     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11501     easing: 'easeOut',
11502     duration: .5
11503 });
11504 </code></pre>
11505     * @param {Object} options (optional) Object literal with any of the Fx config options
11506     * @return {Roo.Element} The Element
11507     */
11508     fadeIn : function(o){
11509         var el = this.getFxEl();
11510         o = o || {};
11511         el.queueFx(o, function(){
11512             this.setOpacity(0);
11513             this.fixDisplay();
11514             this.dom.style.visibility = 'visible';
11515             var to = o.endOpacity || 1;
11516             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11517                 o, null, .5, "easeOut", function(){
11518                 if(to == 1){
11519                     this.clearOpacity();
11520                 }
11521                 el.afterFx(o);
11522             });
11523         });
11524         return this;
11525     },
11526
11527    /**
11528     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11529     * using the "endOpacity" config option.
11530     * Usage:
11531 <pre><code>
11532 // default: fade out from the element's current opacity to 0
11533 el.fadeOut();
11534
11535 // custom: fade out from the element's current opacity to 25% over 2 seconds
11536 el.fadeOut({ endOpacity: .25, duration: 2});
11537
11538 // common config options shown with default values
11539 el.fadeOut({
11540     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11541     easing: 'easeOut',
11542     duration: .5
11543     remove: false,
11544     useDisplay: false
11545 });
11546 </code></pre>
11547     * @param {Object} options (optional) Object literal with any of the Fx config options
11548     * @return {Roo.Element} The Element
11549     */
11550     fadeOut : function(o){
11551         var el = this.getFxEl();
11552         o = o || {};
11553         el.queueFx(o, function(){
11554             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11555                 o, null, .5, "easeOut", function(){
11556                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11557                      this.dom.style.display = "none";
11558                 }else{
11559                      this.dom.style.visibility = "hidden";
11560                 }
11561                 this.clearOpacity();
11562                 el.afterFx(o);
11563             });
11564         });
11565         return this;
11566     },
11567
11568    /**
11569     * Animates the transition of an element's dimensions from a starting height/width
11570     * to an ending height/width.
11571     * Usage:
11572 <pre><code>
11573 // change height and width to 100x100 pixels
11574 el.scale(100, 100);
11575
11576 // common config options shown with default values.  The height and width will default to
11577 // the element's existing values if passed as null.
11578 el.scale(
11579     [element's width],
11580     [element's height], {
11581     easing: 'easeOut',
11582     duration: .35
11583 });
11584 </code></pre>
11585     * @param {Number} width  The new width (pass undefined to keep the original width)
11586     * @param {Number} height  The new height (pass undefined to keep the original height)
11587     * @param {Object} options (optional) Object literal with any of the Fx config options
11588     * @return {Roo.Element} The Element
11589     */
11590     scale : function(w, h, o){
11591         this.shift(Roo.apply({}, o, {
11592             width: w,
11593             height: h
11594         }));
11595         return this;
11596     },
11597
11598    /**
11599     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11600     * Any of these properties not specified in the config object will not be changed.  This effect 
11601     * requires that at least one new dimension, position or opacity setting must be passed in on
11602     * the config object in order for the function to have any effect.
11603     * Usage:
11604 <pre><code>
11605 // slide the element horizontally to x position 200 while changing the height and opacity
11606 el.shift({ x: 200, height: 50, opacity: .8 });
11607
11608 // common config options shown with default values.
11609 el.shift({
11610     width: [element's width],
11611     height: [element's height],
11612     x: [element's x position],
11613     y: [element's y position],
11614     opacity: [element's opacity],
11615     easing: 'easeOut',
11616     duration: .35
11617 });
11618 </code></pre>
11619     * @param {Object} options  Object literal with any of the Fx config options
11620     * @return {Roo.Element} The Element
11621     */
11622     shift : function(o){
11623         var el = this.getFxEl();
11624         o = o || {};
11625         el.queueFx(o, function(){
11626             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11627             if(w !== undefined){
11628                 a.width = {to: this.adjustWidth(w)};
11629             }
11630             if(h !== undefined){
11631                 a.height = {to: this.adjustHeight(h)};
11632             }
11633             if(x !== undefined || y !== undefined){
11634                 a.points = {to: [
11635                     x !== undefined ? x : this.getX(),
11636                     y !== undefined ? y : this.getY()
11637                 ]};
11638             }
11639             if(op !== undefined){
11640                 a.opacity = {to: op};
11641             }
11642             if(o.xy !== undefined){
11643                 a.points = {to: o.xy};
11644             }
11645             arguments.callee.anim = this.fxanim(a,
11646                 o, 'motion', .35, "easeOut", function(){
11647                 el.afterFx(o);
11648             });
11649         });
11650         return this;
11651     },
11652
11653         /**
11654          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11655          * ending point of the effect.
11656          * Usage:
11657          *<pre><code>
11658 // default: slide the element downward while fading out
11659 el.ghost();
11660
11661 // custom: slide the element out to the right with a 2-second duration
11662 el.ghost('r', { duration: 2 });
11663
11664 // common config options shown with default values
11665 el.ghost('b', {
11666     easing: 'easeOut',
11667     duration: .5
11668     remove: false,
11669     useDisplay: false
11670 });
11671 </code></pre>
11672          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11673          * @param {Object} options (optional) Object literal with any of the Fx config options
11674          * @return {Roo.Element} The Element
11675          */
11676     ghost : function(anchor, o){
11677         var el = this.getFxEl();
11678         o = o || {};
11679
11680         el.queueFx(o, function(){
11681             anchor = anchor || "b";
11682
11683             // restore values after effect
11684             var r = this.getFxRestore();
11685             var w = this.getWidth(),
11686                 h = this.getHeight();
11687
11688             var st = this.dom.style;
11689
11690             var after = function(){
11691                 if(o.useDisplay){
11692                     el.setDisplayed(false);
11693                 }else{
11694                     el.hide();
11695                 }
11696
11697                 el.clearOpacity();
11698                 el.setPositioning(r.pos);
11699                 st.width = r.width;
11700                 st.height = r.height;
11701
11702                 el.afterFx(o);
11703             };
11704
11705             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11706             switch(anchor.toLowerCase()){
11707                 case "t":
11708                     pt.by = [0, -h];
11709                 break;
11710                 case "l":
11711                     pt.by = [-w, 0];
11712                 break;
11713                 case "r":
11714                     pt.by = [w, 0];
11715                 break;
11716                 case "b":
11717                     pt.by = [0, h];
11718                 break;
11719                 case "tl":
11720                     pt.by = [-w, -h];
11721                 break;
11722                 case "bl":
11723                     pt.by = [-w, h];
11724                 break;
11725                 case "br":
11726                     pt.by = [w, h];
11727                 break;
11728                 case "tr":
11729                     pt.by = [w, -h];
11730                 break;
11731             }
11732
11733             arguments.callee.anim = this.fxanim(a,
11734                 o,
11735                 'motion',
11736                 .5,
11737                 "easeOut", after);
11738         });
11739         return this;
11740     },
11741
11742         /**
11743          * Ensures that all effects queued after syncFx is called on the element are
11744          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11745          * @return {Roo.Element} The Element
11746          */
11747     syncFx : function(){
11748         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11749             block : false,
11750             concurrent : true,
11751             stopFx : false
11752         });
11753         return this;
11754     },
11755
11756         /**
11757          * Ensures that all effects queued after sequenceFx is called on the element are
11758          * run in sequence.  This is the opposite of {@link #syncFx}.
11759          * @return {Roo.Element} The Element
11760          */
11761     sequenceFx : function(){
11762         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11763             block : false,
11764             concurrent : false,
11765             stopFx : false
11766         });
11767         return this;
11768     },
11769
11770         /* @private */
11771     nextFx : function(){
11772         var ef = this.fxQueue[0];
11773         if(ef){
11774             ef.call(this);
11775         }
11776     },
11777
11778         /**
11779          * Returns true if the element has any effects actively running or queued, else returns false.
11780          * @return {Boolean} True if element has active effects, else false
11781          */
11782     hasActiveFx : function(){
11783         return this.fxQueue && this.fxQueue[0];
11784     },
11785
11786         /**
11787          * Stops any running effects and clears the element's internal effects queue if it contains
11788          * any additional effects that haven't started yet.
11789          * @return {Roo.Element} The Element
11790          */
11791     stopFx : function(){
11792         if(this.hasActiveFx()){
11793             var cur = this.fxQueue[0];
11794             if(cur && cur.anim && cur.anim.isAnimated()){
11795                 this.fxQueue = [cur]; // clear out others
11796                 cur.anim.stop(true);
11797             }
11798         }
11799         return this;
11800     },
11801
11802         /* @private */
11803     beforeFx : function(o){
11804         if(this.hasActiveFx() && !o.concurrent){
11805            if(o.stopFx){
11806                this.stopFx();
11807                return true;
11808            }
11809            return false;
11810         }
11811         return true;
11812     },
11813
11814         /**
11815          * Returns true if the element is currently blocking so that no other effect can be queued
11816          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11817          * used to ensure that an effect initiated by a user action runs to completion prior to the
11818          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11819          * @return {Boolean} True if blocking, else false
11820          */
11821     hasFxBlock : function(){
11822         var q = this.fxQueue;
11823         return q && q[0] && q[0].block;
11824     },
11825
11826         /* @private */
11827     queueFx : function(o, fn){
11828         if(!this.fxQueue){
11829             this.fxQueue = [];
11830         }
11831         if(!this.hasFxBlock()){
11832             Roo.applyIf(o, this.fxDefaults);
11833             if(!o.concurrent){
11834                 var run = this.beforeFx(o);
11835                 fn.block = o.block;
11836                 this.fxQueue.push(fn);
11837                 if(run){
11838                     this.nextFx();
11839                 }
11840             }else{
11841                 fn.call(this);
11842             }
11843         }
11844         return this;
11845     },
11846
11847         /* @private */
11848     fxWrap : function(pos, o, vis){
11849         var wrap;
11850         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11851             var wrapXY;
11852             if(o.fixPosition){
11853                 wrapXY = this.getXY();
11854             }
11855             var div = document.createElement("div");
11856             div.style.visibility = vis;
11857             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11858             wrap.setPositioning(pos);
11859             if(wrap.getStyle("position") == "static"){
11860                 wrap.position("relative");
11861             }
11862             this.clearPositioning('auto');
11863             wrap.clip();
11864             wrap.dom.appendChild(this.dom);
11865             if(wrapXY){
11866                 wrap.setXY(wrapXY);
11867             }
11868         }
11869         return wrap;
11870     },
11871
11872         /* @private */
11873     fxUnwrap : function(wrap, pos, o){
11874         this.clearPositioning();
11875         this.setPositioning(pos);
11876         if(!o.wrap){
11877             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11878             wrap.remove();
11879         }
11880     },
11881
11882         /* @private */
11883     getFxRestore : function(){
11884         var st = this.dom.style;
11885         return {pos: this.getPositioning(), width: st.width, height : st.height};
11886     },
11887
11888         /* @private */
11889     afterFx : function(o){
11890         if(o.afterStyle){
11891             this.applyStyles(o.afterStyle);
11892         }
11893         if(o.afterCls){
11894             this.addClass(o.afterCls);
11895         }
11896         if(o.remove === true){
11897             this.remove();
11898         }
11899         Roo.callback(o.callback, o.scope, [this]);
11900         if(!o.concurrent){
11901             this.fxQueue.shift();
11902             this.nextFx();
11903         }
11904     },
11905
11906         /* @private */
11907     getFxEl : function(){ // support for composite element fx
11908         return Roo.get(this.dom);
11909     },
11910
11911         /* @private */
11912     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11913         animType = animType || 'run';
11914         opt = opt || {};
11915         var anim = Roo.lib.Anim[animType](
11916             this.dom, args,
11917             (opt.duration || defaultDur) || .35,
11918             (opt.easing || defaultEase) || 'easeOut',
11919             function(){
11920                 Roo.callback(cb, this);
11921             },
11922             this
11923         );
11924         opt.anim = anim;
11925         return anim;
11926     }
11927 };
11928
11929 // backwords compat
11930 Roo.Fx.resize = Roo.Fx.scale;
11931
11932 //When included, Roo.Fx is automatically applied to Element so that all basic
11933 //effects are available directly via the Element API
11934 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11935  * Based on:
11936  * Ext JS Library 1.1.1
11937  * Copyright(c) 2006-2007, Ext JS, LLC.
11938  *
11939  * Originally Released Under LGPL - original licence link has changed is not relivant.
11940  *
11941  * Fork - LGPL
11942  * <script type="text/javascript">
11943  */
11944
11945
11946 /**
11947  * @class Roo.CompositeElement
11948  * Standard composite class. Creates a Roo.Element for every element in the collection.
11949  * <br><br>
11950  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11951  * actions will be performed on all the elements in this collection.</b>
11952  * <br><br>
11953  * All methods return <i>this</i> and can be chained.
11954  <pre><code>
11955  var els = Roo.select("#some-el div.some-class", true);
11956  // or select directly from an existing element
11957  var el = Roo.get('some-el');
11958  el.select('div.some-class', true);
11959
11960  els.setWidth(100); // all elements become 100 width
11961  els.hide(true); // all elements fade out and hide
11962  // or
11963  els.setWidth(100).hide(true);
11964  </code></pre>
11965  */
11966 Roo.CompositeElement = function(els){
11967     this.elements = [];
11968     this.addElements(els);
11969 };
11970 Roo.CompositeElement.prototype = {
11971     isComposite: true,
11972     addElements : function(els){
11973         if(!els) {
11974             return this;
11975         }
11976         if(typeof els == "string"){
11977             els = Roo.Element.selectorFunction(els);
11978         }
11979         var yels = this.elements;
11980         var index = yels.length-1;
11981         for(var i = 0, len = els.length; i < len; i++) {
11982                 yels[++index] = Roo.get(els[i]);
11983         }
11984         return this;
11985     },
11986
11987     /**
11988     * Clears this composite and adds the elements returned by the passed selector.
11989     * @param {String/Array} els A string CSS selector, an array of elements or an element
11990     * @return {CompositeElement} this
11991     */
11992     fill : function(els){
11993         this.elements = [];
11994         this.add(els);
11995         return this;
11996     },
11997
11998     /**
11999     * Filters this composite to only elements that match the passed selector.
12000     * @param {String} selector A string CSS selector
12001     * @param {Boolean} inverse return inverse filter (not matches)
12002     * @return {CompositeElement} this
12003     */
12004     filter : function(selector, inverse){
12005         var els = [];
12006         inverse = inverse || false;
12007         this.each(function(el){
12008             var match = inverse ? !el.is(selector) : el.is(selector);
12009             if(match){
12010                 els[els.length] = el.dom;
12011             }
12012         });
12013         this.fill(els);
12014         return this;
12015     },
12016
12017     invoke : function(fn, args){
12018         var els = this.elements;
12019         for(var i = 0, len = els.length; i < len; i++) {
12020                 Roo.Element.prototype[fn].apply(els[i], args);
12021         }
12022         return this;
12023     },
12024     /**
12025     * Adds elements to this composite.
12026     * @param {String/Array} els A string CSS selector, an array of elements or an element
12027     * @return {CompositeElement} this
12028     */
12029     add : function(els){
12030         if(typeof els == "string"){
12031             this.addElements(Roo.Element.selectorFunction(els));
12032         }else if(els.length !== undefined){
12033             this.addElements(els);
12034         }else{
12035             this.addElements([els]);
12036         }
12037         return this;
12038     },
12039     /**
12040     * Calls the passed function passing (el, this, index) for each element in this composite.
12041     * @param {Function} fn The function to call
12042     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12043     * @return {CompositeElement} this
12044     */
12045     each : function(fn, scope){
12046         var els = this.elements;
12047         for(var i = 0, len = els.length; i < len; i++){
12048             if(fn.call(scope || els[i], els[i], this, i) === false) {
12049                 break;
12050             }
12051         }
12052         return this;
12053     },
12054
12055     /**
12056      * Returns the Element object at the specified index
12057      * @param {Number} index
12058      * @return {Roo.Element}
12059      */
12060     item : function(index){
12061         return this.elements[index] || null;
12062     },
12063
12064     /**
12065      * Returns the first Element
12066      * @return {Roo.Element}
12067      */
12068     first : function(){
12069         return this.item(0);
12070     },
12071
12072     /**
12073      * Returns the last Element
12074      * @return {Roo.Element}
12075      */
12076     last : function(){
12077         return this.item(this.elements.length-1);
12078     },
12079
12080     /**
12081      * Returns the number of elements in this composite
12082      * @return Number
12083      */
12084     getCount : function(){
12085         return this.elements.length;
12086     },
12087
12088     /**
12089      * Returns true if this composite contains the passed element
12090      * @return Boolean
12091      */
12092     contains : function(el){
12093         return this.indexOf(el) !== -1;
12094     },
12095
12096     /**
12097      * Returns true if this composite contains the passed element
12098      * @return Boolean
12099      */
12100     indexOf : function(el){
12101         return this.elements.indexOf(Roo.get(el));
12102     },
12103
12104
12105     /**
12106     * Removes the specified element(s).
12107     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12108     * or an array of any of those.
12109     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12110     * @return {CompositeElement} this
12111     */
12112     removeElement : function(el, removeDom){
12113         if(el instanceof Array){
12114             for(var i = 0, len = el.length; i < len; i++){
12115                 this.removeElement(el[i]);
12116             }
12117             return this;
12118         }
12119         var index = typeof el == 'number' ? el : this.indexOf(el);
12120         if(index !== -1){
12121             if(removeDom){
12122                 var d = this.elements[index];
12123                 if(d.dom){
12124                     d.remove();
12125                 }else{
12126                     d.parentNode.removeChild(d);
12127                 }
12128             }
12129             this.elements.splice(index, 1);
12130         }
12131         return this;
12132     },
12133
12134     /**
12135     * Replaces the specified element with the passed element.
12136     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12137     * to replace.
12138     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12139     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12140     * @return {CompositeElement} this
12141     */
12142     replaceElement : function(el, replacement, domReplace){
12143         var index = typeof el == 'number' ? el : this.indexOf(el);
12144         if(index !== -1){
12145             if(domReplace){
12146                 this.elements[index].replaceWith(replacement);
12147             }else{
12148                 this.elements.splice(index, 1, Roo.get(replacement))
12149             }
12150         }
12151         return this;
12152     },
12153
12154     /**
12155      * Removes all elements.
12156      */
12157     clear : function(){
12158         this.elements = [];
12159     }
12160 };
12161 (function(){
12162     Roo.CompositeElement.createCall = function(proto, fnName){
12163         if(!proto[fnName]){
12164             proto[fnName] = function(){
12165                 return this.invoke(fnName, arguments);
12166             };
12167         }
12168     };
12169     for(var fnName in Roo.Element.prototype){
12170         if(typeof Roo.Element.prototype[fnName] == "function"){
12171             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12172         }
12173     };
12174 })();
12175 /*
12176  * Based on:
12177  * Ext JS Library 1.1.1
12178  * Copyright(c) 2006-2007, Ext JS, LLC.
12179  *
12180  * Originally Released Under LGPL - original licence link has changed is not relivant.
12181  *
12182  * Fork - LGPL
12183  * <script type="text/javascript">
12184  */
12185
12186 /**
12187  * @class Roo.CompositeElementLite
12188  * @extends Roo.CompositeElement
12189  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12190  <pre><code>
12191  var els = Roo.select("#some-el div.some-class");
12192  // or select directly from an existing element
12193  var el = Roo.get('some-el');
12194  el.select('div.some-class');
12195
12196  els.setWidth(100); // all elements become 100 width
12197  els.hide(true); // all elements fade out and hide
12198  // or
12199  els.setWidth(100).hide(true);
12200  </code></pre><br><br>
12201  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12202  * actions will be performed on all the elements in this collection.</b>
12203  */
12204 Roo.CompositeElementLite = function(els){
12205     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12206     this.el = new Roo.Element.Flyweight();
12207 };
12208 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12209     addElements : function(els){
12210         if(els){
12211             if(els instanceof Array){
12212                 this.elements = this.elements.concat(els);
12213             }else{
12214                 var yels = this.elements;
12215                 var index = yels.length-1;
12216                 for(var i = 0, len = els.length; i < len; i++) {
12217                     yels[++index] = els[i];
12218                 }
12219             }
12220         }
12221         return this;
12222     },
12223     invoke : function(fn, args){
12224         var els = this.elements;
12225         var el = this.el;
12226         for(var i = 0, len = els.length; i < len; i++) {
12227             el.dom = els[i];
12228                 Roo.Element.prototype[fn].apply(el, args);
12229         }
12230         return this;
12231     },
12232     /**
12233      * Returns a flyweight Element of the dom element object at the specified index
12234      * @param {Number} index
12235      * @return {Roo.Element}
12236      */
12237     item : function(index){
12238         if(!this.elements[index]){
12239             return null;
12240         }
12241         this.el.dom = this.elements[index];
12242         return this.el;
12243     },
12244
12245     // fixes scope with flyweight
12246     addListener : function(eventName, handler, scope, opt){
12247         var els = this.elements;
12248         for(var i = 0, len = els.length; i < len; i++) {
12249             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12250         }
12251         return this;
12252     },
12253
12254     /**
12255     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12256     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12257     * a reference to the dom node, use el.dom.</b>
12258     * @param {Function} fn The function to call
12259     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12260     * @return {CompositeElement} this
12261     */
12262     each : function(fn, scope){
12263         var els = this.elements;
12264         var el = this.el;
12265         for(var i = 0, len = els.length; i < len; i++){
12266             el.dom = els[i];
12267                 if(fn.call(scope || el, el, this, i) === false){
12268                 break;
12269             }
12270         }
12271         return this;
12272     },
12273
12274     indexOf : function(el){
12275         return this.elements.indexOf(Roo.getDom(el));
12276     },
12277
12278     replaceElement : function(el, replacement, domReplace){
12279         var index = typeof el == 'number' ? el : this.indexOf(el);
12280         if(index !== -1){
12281             replacement = Roo.getDom(replacement);
12282             if(domReplace){
12283                 var d = this.elements[index];
12284                 d.parentNode.insertBefore(replacement, d);
12285                 d.parentNode.removeChild(d);
12286             }
12287             this.elements.splice(index, 1, replacement);
12288         }
12289         return this;
12290     }
12291 });
12292 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12293
12294 /*
12295  * Based on:
12296  * Ext JS Library 1.1.1
12297  * Copyright(c) 2006-2007, Ext JS, LLC.
12298  *
12299  * Originally Released Under LGPL - original licence link has changed is not relivant.
12300  *
12301  * Fork - LGPL
12302  * <script type="text/javascript">
12303  */
12304
12305  
12306
12307 /**
12308  * @class Roo.data.Connection
12309  * @extends Roo.util.Observable
12310  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12311  * either to a configured URL, or to a URL specified at request time. 
12312  * 
12313  * Requests made by this class are asynchronous, and will return immediately. No data from
12314  * the server will be available to the statement immediately following the {@link #request} call.
12315  * To process returned data, use a callback in the request options object, or an event listener.
12316  * 
12317  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12318  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12319  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12320  * property and, if present, the IFRAME's XML document as the responseXML property.
12321  * 
12322  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12323  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12324  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12325  * standard DOM methods.
12326  * @constructor
12327  * @param {Object} config a configuration object.
12328  */
12329 Roo.data.Connection = function(config){
12330     Roo.apply(this, config);
12331     this.addEvents({
12332         /**
12333          * @event beforerequest
12334          * Fires before a network request is made to retrieve a data object.
12335          * @param {Connection} conn This Connection object.
12336          * @param {Object} options The options config object passed to the {@link #request} method.
12337          */
12338         "beforerequest" : true,
12339         /**
12340          * @event requestcomplete
12341          * Fires if the request was successfully completed.
12342          * @param {Connection} conn This Connection object.
12343          * @param {Object} response The XHR object containing the response data.
12344          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12345          * @param {Object} options The options config object passed to the {@link #request} method.
12346          */
12347         "requestcomplete" : true,
12348         /**
12349          * @event requestexception
12350          * Fires if an error HTTP status was returned from the server.
12351          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12352          * @param {Connection} conn This Connection object.
12353          * @param {Object} response The XHR object containing the response data.
12354          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12355          * @param {Object} options The options config object passed to the {@link #request} method.
12356          */
12357         "requestexception" : true
12358     });
12359     Roo.data.Connection.superclass.constructor.call(this);
12360 };
12361
12362 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12363     /**
12364      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12365      */
12366     /**
12367      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12368      * extra parameters to each request made by this object. (defaults to undefined)
12369      */
12370     /**
12371      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12372      *  to each request made by this object. (defaults to undefined)
12373      */
12374     /**
12375      * @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)
12376      */
12377     /**
12378      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12379      */
12380     timeout : 30000,
12381     /**
12382      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12383      * @type Boolean
12384      */
12385     autoAbort:false,
12386
12387     /**
12388      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12389      * @type Boolean
12390      */
12391     disableCaching: true,
12392
12393     /**
12394      * Sends an HTTP request to a remote server.
12395      * @param {Object} options An object which may contain the following properties:<ul>
12396      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12397      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12398      * request, a url encoded string or a function to call to get either.</li>
12399      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12400      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12401      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12402      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12403      * <li>options {Object} The parameter to the request call.</li>
12404      * <li>success {Boolean} True if the request succeeded.</li>
12405      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12406      * </ul></li>
12407      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12408      * The callback is passed the following parameters:<ul>
12409      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12410      * <li>options {Object} The parameter to the request call.</li>
12411      * </ul></li>
12412      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12413      * The callback is passed the following parameters:<ul>
12414      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12415      * <li>options {Object} The parameter to the request call.</li>
12416      * </ul></li>
12417      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12418      * for the callback function. Defaults to the browser window.</li>
12419      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12420      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12421      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12422      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12423      * params for the post data. Any params will be appended to the URL.</li>
12424      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12425      * </ul>
12426      * @return {Number} transactionId
12427      */
12428     request : function(o){
12429         if(this.fireEvent("beforerequest", this, o) !== false){
12430             var p = o.params;
12431
12432             if(typeof p == "function"){
12433                 p = p.call(o.scope||window, o);
12434             }
12435             if(typeof p == "object"){
12436                 p = Roo.urlEncode(o.params);
12437             }
12438             if(this.extraParams){
12439                 var extras = Roo.urlEncode(this.extraParams);
12440                 p = p ? (p + '&' + extras) : extras;
12441             }
12442
12443             var url = o.url || this.url;
12444             if(typeof url == 'function'){
12445                 url = url.call(o.scope||window, o);
12446             }
12447
12448             if(o.form){
12449                 var form = Roo.getDom(o.form);
12450                 url = url || form.action;
12451
12452                 var enctype = form.getAttribute("enctype");
12453                 
12454                 if (o.formData) {
12455                     return this.doFormDataUpload(o, url);
12456                 }
12457                 
12458                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12459                     return this.doFormUpload(o, p, url);
12460                 }
12461                 var f = Roo.lib.Ajax.serializeForm(form);
12462                 p = p ? (p + '&' + f) : f;
12463             }
12464             
12465             if (!o.form && o.formData) {
12466                 o.formData = o.formData === true ? new FormData() : o.formData;
12467                 for (var k in o.params) {
12468                     o.formData.append(k,o.params[k]);
12469                 }
12470                     
12471                 return this.doFormDataUpload(o, url);
12472             }
12473             
12474
12475             var hs = o.headers;
12476             if(this.defaultHeaders){
12477                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12478                 if(!o.headers){
12479                     o.headers = hs;
12480                 }
12481             }
12482
12483             var cb = {
12484                 success: this.handleResponse,
12485                 failure: this.handleFailure,
12486                 scope: this,
12487                 argument: {options: o},
12488                 timeout : o.timeout || this.timeout
12489             };
12490
12491             var method = o.method||this.method||(p ? "POST" : "GET");
12492
12493             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12494                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12495             }
12496
12497             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12498                 if(o.autoAbort){
12499                     this.abort();
12500                 }
12501             }else if(this.autoAbort !== false){
12502                 this.abort();
12503             }
12504
12505             if((method == 'GET' && p) || o.xmlData){
12506                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12507                 p = '';
12508             }
12509             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12510             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12511             Roo.lib.Ajax.useDefaultHeader == true;
12512             return this.transId;
12513         }else{
12514             Roo.callback(o.callback, o.scope, [o, null, null]);
12515             return null;
12516         }
12517     },
12518
12519     /**
12520      * Determine whether this object has a request outstanding.
12521      * @param {Number} transactionId (Optional) defaults to the last transaction
12522      * @return {Boolean} True if there is an outstanding request.
12523      */
12524     isLoading : function(transId){
12525         if(transId){
12526             return Roo.lib.Ajax.isCallInProgress(transId);
12527         }else{
12528             return this.transId ? true : false;
12529         }
12530     },
12531
12532     /**
12533      * Aborts any outstanding request.
12534      * @param {Number} transactionId (Optional) defaults to the last transaction
12535      */
12536     abort : function(transId){
12537         if(transId || this.isLoading()){
12538             Roo.lib.Ajax.abort(transId || this.transId);
12539         }
12540     },
12541
12542     // private
12543     handleResponse : function(response){
12544         this.transId = false;
12545         var options = response.argument.options;
12546         response.argument = options ? options.argument : null;
12547         this.fireEvent("requestcomplete", this, response, options);
12548         Roo.callback(options.success, options.scope, [response, options]);
12549         Roo.callback(options.callback, options.scope, [options, true, response]);
12550     },
12551
12552     // private
12553     handleFailure : function(response, e){
12554         this.transId = false;
12555         var options = response.argument.options;
12556         response.argument = options ? options.argument : null;
12557         this.fireEvent("requestexception", this, response, options, e);
12558         Roo.callback(options.failure, options.scope, [response, options]);
12559         Roo.callback(options.callback, options.scope, [options, false, response]);
12560     },
12561
12562     // private
12563     doFormUpload : function(o, ps, url){
12564         var id = Roo.id();
12565         var frame = document.createElement('iframe');
12566         frame.id = id;
12567         frame.name = id;
12568         frame.className = 'x-hidden';
12569         if(Roo.isIE){
12570             frame.src = Roo.SSL_SECURE_URL;
12571         }
12572         document.body.appendChild(frame);
12573
12574         if(Roo.isIE){
12575            document.frames[id].name = id;
12576         }
12577
12578         var form = Roo.getDom(o.form);
12579         form.target = id;
12580         form.method = 'POST';
12581         form.enctype = form.encoding = 'multipart/form-data';
12582         if(url){
12583             form.action = url;
12584         }
12585
12586         var hiddens, hd;
12587         if(ps){ // add dynamic params
12588             hiddens = [];
12589             ps = Roo.urlDecode(ps, false);
12590             for(var k in ps){
12591                 if(ps.hasOwnProperty(k)){
12592                     hd = document.createElement('input');
12593                     hd.type = 'hidden';
12594                     hd.name = k;
12595                     hd.value = ps[k];
12596                     form.appendChild(hd);
12597                     hiddens.push(hd);
12598                 }
12599             }
12600         }
12601
12602         function cb(){
12603             var r = {  // bogus response object
12604                 responseText : '',
12605                 responseXML : null
12606             };
12607
12608             r.argument = o ? o.argument : null;
12609
12610             try { //
12611                 var doc;
12612                 if(Roo.isIE){
12613                     doc = frame.contentWindow.document;
12614                 }else {
12615                     doc = (frame.contentDocument || window.frames[id].document);
12616                 }
12617                 if(doc && doc.body){
12618                     r.responseText = doc.body.innerHTML;
12619                 }
12620                 if(doc && doc.XMLDocument){
12621                     r.responseXML = doc.XMLDocument;
12622                 }else {
12623                     r.responseXML = doc;
12624                 }
12625             }
12626             catch(e) {
12627                 // ignore
12628             }
12629
12630             Roo.EventManager.removeListener(frame, 'load', cb, this);
12631
12632             this.fireEvent("requestcomplete", this, r, o);
12633             Roo.callback(o.success, o.scope, [r, o]);
12634             Roo.callback(o.callback, o.scope, [o, true, r]);
12635
12636             setTimeout(function(){document.body.removeChild(frame);}, 100);
12637         }
12638
12639         Roo.EventManager.on(frame, 'load', cb, this);
12640         form.submit();
12641
12642         if(hiddens){ // remove dynamic params
12643             for(var i = 0, len = hiddens.length; i < len; i++){
12644                 form.removeChild(hiddens[i]);
12645             }
12646         }
12647     },
12648     // this is a 'formdata version???'
12649     
12650     
12651     doFormDataUpload : function(o,  url)
12652     {
12653         var formData;
12654         if (o.form) {
12655             var form =  Roo.getDom(o.form);
12656             form.enctype = form.encoding = 'multipart/form-data';
12657             formData = o.formData === true ? new FormData(form) : o.formData;
12658         } else {
12659             formData = o.formData === true ? new FormData() : o.formData;
12660         }
12661         
12662       
12663         var cb = {
12664             success: this.handleResponse,
12665             failure: this.handleFailure,
12666             scope: this,
12667             argument: {options: o},
12668             timeout : o.timeout || this.timeout
12669         };
12670  
12671         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12672             if(o.autoAbort){
12673                 this.abort();
12674             }
12675         }else if(this.autoAbort !== false){
12676             this.abort();
12677         }
12678
12679         //Roo.lib.Ajax.defaultPostHeader = null;
12680         Roo.lib.Ajax.useDefaultHeader = false;
12681         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12682         Roo.lib.Ajax.useDefaultHeader = true;
12683  
12684          
12685     }
12686     
12687 });
12688 /*
12689  * Based on:
12690  * Ext JS Library 1.1.1
12691  * Copyright(c) 2006-2007, Ext JS, LLC.
12692  *
12693  * Originally Released Under LGPL - original licence link has changed is not relivant.
12694  *
12695  * Fork - LGPL
12696  * <script type="text/javascript">
12697  */
12698  
12699 /**
12700  * Global Ajax request class.
12701  * 
12702  * @class Roo.Ajax
12703  * @extends Roo.data.Connection
12704  * @static
12705  * 
12706  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12707  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12708  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12709  * @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)
12710  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12711  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12712  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12713  */
12714 Roo.Ajax = new Roo.data.Connection({
12715     // fix up the docs
12716     /**
12717      * @scope Roo.Ajax
12718      * @type {Boolear} 
12719      */
12720     autoAbort : false,
12721
12722     /**
12723      * Serialize the passed form into a url encoded string
12724      * @scope Roo.Ajax
12725      * @param {String/HTMLElement} form
12726      * @return {String}
12727      */
12728     serializeForm : function(form){
12729         return Roo.lib.Ajax.serializeForm(form);
12730     }
12731 });/*
12732  * Based on:
12733  * Ext JS Library 1.1.1
12734  * Copyright(c) 2006-2007, Ext JS, LLC.
12735  *
12736  * Originally Released Under LGPL - original licence link has changed is not relivant.
12737  *
12738  * Fork - LGPL
12739  * <script type="text/javascript">
12740  */
12741
12742  
12743 /**
12744  * @class Roo.UpdateManager
12745  * @extends Roo.util.Observable
12746  * Provides AJAX-style update for Element object.<br><br>
12747  * Usage:<br>
12748  * <pre><code>
12749  * // Get it from a Roo.Element object
12750  * var el = Roo.get("foo");
12751  * var mgr = el.getUpdateManager();
12752  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12753  * ...
12754  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12755  * <br>
12756  * // or directly (returns the same UpdateManager instance)
12757  * var mgr = new Roo.UpdateManager("myElementId");
12758  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12759  * mgr.on("update", myFcnNeedsToKnow);
12760  * <br>
12761    // short handed call directly from the element object
12762    Roo.get("foo").load({
12763         url: "bar.php",
12764         scripts:true,
12765         params: "for=bar",
12766         text: "Loading Foo..."
12767    });
12768  * </code></pre>
12769  * @constructor
12770  * Create new UpdateManager directly.
12771  * @param {String/HTMLElement/Roo.Element} el The element to update
12772  * @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).
12773  */
12774 Roo.UpdateManager = function(el, forceNew){
12775     el = Roo.get(el);
12776     if(!forceNew && el.updateManager){
12777         return el.updateManager;
12778     }
12779     /**
12780      * The Element object
12781      * @type Roo.Element
12782      */
12783     this.el = el;
12784     /**
12785      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12786      * @type String
12787      */
12788     this.defaultUrl = null;
12789
12790     this.addEvents({
12791         /**
12792          * @event beforeupdate
12793          * Fired before an update is made, return false from your handler and the update is cancelled.
12794          * @param {Roo.Element} el
12795          * @param {String/Object/Function} url
12796          * @param {String/Object} params
12797          */
12798         "beforeupdate": true,
12799         /**
12800          * @event update
12801          * Fired after successful update is made.
12802          * @param {Roo.Element} el
12803          * @param {Object} oResponseObject The response Object
12804          */
12805         "update": true,
12806         /**
12807          * @event failure
12808          * Fired on update failure.
12809          * @param {Roo.Element} el
12810          * @param {Object} oResponseObject The response Object
12811          */
12812         "failure": true
12813     });
12814     var d = Roo.UpdateManager.defaults;
12815     /**
12816      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12817      * @type String
12818      */
12819     this.sslBlankUrl = d.sslBlankUrl;
12820     /**
12821      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12822      * @type Boolean
12823      */
12824     this.disableCaching = d.disableCaching;
12825     /**
12826      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12827      * @type String
12828      */
12829     this.indicatorText = d.indicatorText;
12830     /**
12831      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12832      * @type String
12833      */
12834     this.showLoadIndicator = d.showLoadIndicator;
12835     /**
12836      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12837      * @type Number
12838      */
12839     this.timeout = d.timeout;
12840
12841     /**
12842      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12843      * @type Boolean
12844      */
12845     this.loadScripts = d.loadScripts;
12846
12847     /**
12848      * Transaction object of current executing transaction
12849      */
12850     this.transaction = null;
12851
12852     /**
12853      * @private
12854      */
12855     this.autoRefreshProcId = null;
12856     /**
12857      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12858      * @type Function
12859      */
12860     this.refreshDelegate = this.refresh.createDelegate(this);
12861     /**
12862      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12863      * @type Function
12864      */
12865     this.updateDelegate = this.update.createDelegate(this);
12866     /**
12867      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12868      * @type Function
12869      */
12870     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12871     /**
12872      * @private
12873      */
12874     this.successDelegate = this.processSuccess.createDelegate(this);
12875     /**
12876      * @private
12877      */
12878     this.failureDelegate = this.processFailure.createDelegate(this);
12879
12880     if(!this.renderer){
12881      /**
12882       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12883       */
12884     this.renderer = new Roo.UpdateManager.BasicRenderer();
12885     }
12886     
12887     Roo.UpdateManager.superclass.constructor.call(this);
12888 };
12889
12890 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12891     /**
12892      * Get the Element this UpdateManager is bound to
12893      * @return {Roo.Element} The element
12894      */
12895     getEl : function(){
12896         return this.el;
12897     },
12898     /**
12899      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12900      * @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:
12901 <pre><code>
12902 um.update({<br/>
12903     url: "your-url.php",<br/>
12904     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12905     callback: yourFunction,<br/>
12906     scope: yourObject, //(optional scope)  <br/>
12907     discardUrl: false, <br/>
12908     nocache: false,<br/>
12909     text: "Loading...",<br/>
12910     timeout: 30,<br/>
12911     scripts: false<br/>
12912 });
12913 </code></pre>
12914      * The only required property is url. The optional properties nocache, text and scripts
12915      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12916      * @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}
12917      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12918      * @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.
12919      */
12920     update : function(url, params, callback, discardUrl){
12921         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12922             var method = this.method,
12923                 cfg;
12924             if(typeof url == "object"){ // must be config object
12925                 cfg = url;
12926                 url = cfg.url;
12927                 params = params || cfg.params;
12928                 callback = callback || cfg.callback;
12929                 discardUrl = discardUrl || cfg.discardUrl;
12930                 if(callback && cfg.scope){
12931                     callback = callback.createDelegate(cfg.scope);
12932                 }
12933                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12934                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12935                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12936                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12937                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12938             }
12939             this.showLoading();
12940             if(!discardUrl){
12941                 this.defaultUrl = url;
12942             }
12943             if(typeof url == "function"){
12944                 url = url.call(this);
12945             }
12946
12947             method = method || (params ? "POST" : "GET");
12948             if(method == "GET"){
12949                 url = this.prepareUrl(url);
12950             }
12951
12952             var o = Roo.apply(cfg ||{}, {
12953                 url : url,
12954                 params: params,
12955                 success: this.successDelegate,
12956                 failure: this.failureDelegate,
12957                 callback: undefined,
12958                 timeout: (this.timeout*1000),
12959                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12960             });
12961             Roo.log("updated manager called with timeout of " + o.timeout);
12962             this.transaction = Roo.Ajax.request(o);
12963         }
12964     },
12965
12966     /**
12967      * 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.
12968      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12969      * @param {String/HTMLElement} form The form Id or form element
12970      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12971      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12972      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12973      */
12974     formUpdate : function(form, url, reset, callback){
12975         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12976             if(typeof url == "function"){
12977                 url = url.call(this);
12978             }
12979             form = Roo.getDom(form);
12980             this.transaction = Roo.Ajax.request({
12981                 form: form,
12982                 url:url,
12983                 success: this.successDelegate,
12984                 failure: this.failureDelegate,
12985                 timeout: (this.timeout*1000),
12986                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12987             });
12988             this.showLoading.defer(1, this);
12989         }
12990     },
12991
12992     /**
12993      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12994      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12995      */
12996     refresh : function(callback){
12997         if(this.defaultUrl == null){
12998             return;
12999         }
13000         this.update(this.defaultUrl, null, callback, true);
13001     },
13002
13003     /**
13004      * Set this element to auto refresh.
13005      * @param {Number} interval How often to update (in seconds).
13006      * @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)
13007      * @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}
13008      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13009      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13010      */
13011     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13012         if(refreshNow){
13013             this.update(url || this.defaultUrl, params, callback, true);
13014         }
13015         if(this.autoRefreshProcId){
13016             clearInterval(this.autoRefreshProcId);
13017         }
13018         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13019     },
13020
13021     /**
13022      * Stop auto refresh on this element.
13023      */
13024      stopAutoRefresh : function(){
13025         if(this.autoRefreshProcId){
13026             clearInterval(this.autoRefreshProcId);
13027             delete this.autoRefreshProcId;
13028         }
13029     },
13030
13031     isAutoRefreshing : function(){
13032        return this.autoRefreshProcId ? true : false;
13033     },
13034     /**
13035      * Called to update the element to "Loading" state. Override to perform custom action.
13036      */
13037     showLoading : function(){
13038         if(this.showLoadIndicator){
13039             this.el.update(this.indicatorText);
13040         }
13041     },
13042
13043     /**
13044      * Adds unique parameter to query string if disableCaching = true
13045      * @private
13046      */
13047     prepareUrl : function(url){
13048         if(this.disableCaching){
13049             var append = "_dc=" + (new Date().getTime());
13050             if(url.indexOf("?") !== -1){
13051                 url += "&" + append;
13052             }else{
13053                 url += "?" + append;
13054             }
13055         }
13056         return url;
13057     },
13058
13059     /**
13060      * @private
13061      */
13062     processSuccess : function(response){
13063         this.transaction = null;
13064         if(response.argument.form && response.argument.reset){
13065             try{ // put in try/catch since some older FF releases had problems with this
13066                 response.argument.form.reset();
13067             }catch(e){}
13068         }
13069         if(this.loadScripts){
13070             this.renderer.render(this.el, response, this,
13071                 this.updateComplete.createDelegate(this, [response]));
13072         }else{
13073             this.renderer.render(this.el, response, this);
13074             this.updateComplete(response);
13075         }
13076     },
13077
13078     updateComplete : function(response){
13079         this.fireEvent("update", this.el, response);
13080         if(typeof response.argument.callback == "function"){
13081             response.argument.callback(this.el, true, response);
13082         }
13083     },
13084
13085     /**
13086      * @private
13087      */
13088     processFailure : function(response){
13089         this.transaction = null;
13090         this.fireEvent("failure", this.el, response);
13091         if(typeof response.argument.callback == "function"){
13092             response.argument.callback(this.el, false, response);
13093         }
13094     },
13095
13096     /**
13097      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13098      * @param {Object} renderer The object implementing the render() method
13099      */
13100     setRenderer : function(renderer){
13101         this.renderer = renderer;
13102     },
13103
13104     getRenderer : function(){
13105        return this.renderer;
13106     },
13107
13108     /**
13109      * Set the defaultUrl used for updates
13110      * @param {String/Function} defaultUrl The url or a function to call to get the url
13111      */
13112     setDefaultUrl : function(defaultUrl){
13113         this.defaultUrl = defaultUrl;
13114     },
13115
13116     /**
13117      * Aborts the executing transaction
13118      */
13119     abort : function(){
13120         if(this.transaction){
13121             Roo.Ajax.abort(this.transaction);
13122         }
13123     },
13124
13125     /**
13126      * Returns true if an update is in progress
13127      * @return {Boolean}
13128      */
13129     isUpdating : function(){
13130         if(this.transaction){
13131             return Roo.Ajax.isLoading(this.transaction);
13132         }
13133         return false;
13134     }
13135 });
13136
13137 /**
13138  * @class Roo.UpdateManager.defaults
13139  * @static (not really - but it helps the doc tool)
13140  * The defaults collection enables customizing the default properties of UpdateManager
13141  */
13142    Roo.UpdateManager.defaults = {
13143        /**
13144          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13145          * @type Number
13146          */
13147          timeout : 30,
13148
13149          /**
13150          * True to process scripts by default (Defaults to false).
13151          * @type Boolean
13152          */
13153         loadScripts : false,
13154
13155         /**
13156         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13157         * @type String
13158         */
13159         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13160         /**
13161          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13162          * @type Boolean
13163          */
13164         disableCaching : false,
13165         /**
13166          * Whether to show indicatorText when loading (Defaults to true).
13167          * @type Boolean
13168          */
13169         showLoadIndicator : true,
13170         /**
13171          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13172          * @type String
13173          */
13174         indicatorText : '<div class="loading-indicator">Loading...</div>'
13175    };
13176
13177 /**
13178  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13179  *Usage:
13180  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13181  * @param {String/HTMLElement/Roo.Element} el The element to update
13182  * @param {String} url The url
13183  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13184  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13185  * @static
13186  * @deprecated
13187  * @member Roo.UpdateManager
13188  */
13189 Roo.UpdateManager.updateElement = function(el, url, params, options){
13190     var um = Roo.get(el, true).getUpdateManager();
13191     Roo.apply(um, options);
13192     um.update(url, params, options ? options.callback : null);
13193 };
13194 // alias for backwards compat
13195 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13196 /**
13197  * @class Roo.UpdateManager.BasicRenderer
13198  * Default Content renderer. Updates the elements innerHTML with the responseText.
13199  */
13200 Roo.UpdateManager.BasicRenderer = function(){};
13201
13202 Roo.UpdateManager.BasicRenderer.prototype = {
13203     /**
13204      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13205      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13206      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13207      * @param {Roo.Element} el The element being rendered
13208      * @param {Object} response The YUI Connect response object
13209      * @param {UpdateManager} updateManager The calling update manager
13210      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13211      */
13212      render : function(el, response, updateManager, callback){
13213         el.update(response.responseText, updateManager.loadScripts, callback);
13214     }
13215 };
13216 /*
13217  * Based on:
13218  * Roo JS
13219  * (c)) Alan Knowles
13220  * Licence : LGPL
13221  */
13222
13223
13224 /**
13225  * @class Roo.DomTemplate
13226  * @extends Roo.Template
13227  * An effort at a dom based template engine..
13228  *
13229  * Similar to XTemplate, except it uses dom parsing to create the template..
13230  *
13231  * Supported features:
13232  *
13233  *  Tags:
13234
13235 <pre><code>
13236       {a_variable} - output encoded.
13237       {a_variable.format:("Y-m-d")} - call a method on the variable
13238       {a_variable:raw} - unencoded output
13239       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13240       {a_variable:this.method_on_template(...)} - call a method on the template object.
13241  
13242 </code></pre>
13243  *  The tpl tag:
13244 <pre><code>
13245         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13246         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13247         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13248         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13249   
13250 </code></pre>
13251  *      
13252  */
13253 Roo.DomTemplate = function()
13254 {
13255      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13256      if (this.html) {
13257         this.compile();
13258      }
13259 };
13260
13261
13262 Roo.extend(Roo.DomTemplate, Roo.Template, {
13263     /**
13264      * id counter for sub templates.
13265      */
13266     id : 0,
13267     /**
13268      * flag to indicate if dom parser is inside a pre,
13269      * it will strip whitespace if not.
13270      */
13271     inPre : false,
13272     
13273     /**
13274      * The various sub templates
13275      */
13276     tpls : false,
13277     
13278     
13279     
13280     /**
13281      *
13282      * basic tag replacing syntax
13283      * WORD:WORD()
13284      *
13285      * // you can fake an object call by doing this
13286      *  x.t:(test,tesT) 
13287      * 
13288      */
13289     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13290     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13291     
13292     iterChild : function (node, method) {
13293         
13294         var oldPre = this.inPre;
13295         if (node.tagName == 'PRE') {
13296             this.inPre = true;
13297         }
13298         for( var i = 0; i < node.childNodes.length; i++) {
13299             method.call(this, node.childNodes[i]);
13300         }
13301         this.inPre = oldPre;
13302     },
13303     
13304     
13305     
13306     /**
13307      * compile the template
13308      *
13309      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13310      *
13311      */
13312     compile: function()
13313     {
13314         var s = this.html;
13315         
13316         // covert the html into DOM...
13317         var doc = false;
13318         var div =false;
13319         try {
13320             doc = document.implementation.createHTMLDocument("");
13321             doc.documentElement.innerHTML =   this.html  ;
13322             div = doc.documentElement;
13323         } catch (e) {
13324             // old IE... - nasty -- it causes all sorts of issues.. with
13325             // images getting pulled from server..
13326             div = document.createElement('div');
13327             div.innerHTML = this.html;
13328         }
13329         //doc.documentElement.innerHTML = htmlBody
13330          
13331         
13332         
13333         this.tpls = [];
13334         var _t = this;
13335         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13336         
13337         var tpls = this.tpls;
13338         
13339         // create a top level template from the snippet..
13340         
13341         //Roo.log(div.innerHTML);
13342         
13343         var tpl = {
13344             uid : 'master',
13345             id : this.id++,
13346             attr : false,
13347             value : false,
13348             body : div.innerHTML,
13349             
13350             forCall : false,
13351             execCall : false,
13352             dom : div,
13353             isTop : true
13354             
13355         };
13356         tpls.unshift(tpl);
13357         
13358         
13359         // compile them...
13360         this.tpls = [];
13361         Roo.each(tpls, function(tp){
13362             this.compileTpl(tp);
13363             this.tpls[tp.id] = tp;
13364         }, this);
13365         
13366         this.master = tpls[0];
13367         return this;
13368         
13369         
13370     },
13371     
13372     compileNode : function(node, istop) {
13373         // test for
13374         //Roo.log(node);
13375         
13376         
13377         // skip anything not a tag..
13378         if (node.nodeType != 1) {
13379             if (node.nodeType == 3 && !this.inPre) {
13380                 // reduce white space..
13381                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13382                 
13383             }
13384             return;
13385         }
13386         
13387         var tpl = {
13388             uid : false,
13389             id : false,
13390             attr : false,
13391             value : false,
13392             body : '',
13393             
13394             forCall : false,
13395             execCall : false,
13396             dom : false,
13397             isTop : istop
13398             
13399             
13400         };
13401         
13402         
13403         switch(true) {
13404             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13405             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13406             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13407             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13408             // no default..
13409         }
13410         
13411         
13412         if (!tpl.attr) {
13413             // just itterate children..
13414             this.iterChild(node,this.compileNode);
13415             return;
13416         }
13417         tpl.uid = this.id++;
13418         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13419         node.removeAttribute('roo-'+ tpl.attr);
13420         if (tpl.attr != 'name') {
13421             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13422             node.parentNode.replaceChild(placeholder,  node);
13423         } else {
13424             
13425             var placeholder =  document.createElement('span');
13426             placeholder.className = 'roo-tpl-' + tpl.value;
13427             node.parentNode.replaceChild(placeholder,  node);
13428         }
13429         
13430         // parent now sees '{domtplXXXX}
13431         this.iterChild(node,this.compileNode);
13432         
13433         // we should now have node body...
13434         var div = document.createElement('div');
13435         div.appendChild(node);
13436         tpl.dom = node;
13437         // this has the unfortunate side effect of converting tagged attributes
13438         // eg. href="{...}" into %7C...%7D
13439         // this has been fixed by searching for those combo's although it's a bit hacky..
13440         
13441         
13442         tpl.body = div.innerHTML;
13443         
13444         
13445          
13446         tpl.id = tpl.uid;
13447         switch(tpl.attr) {
13448             case 'for' :
13449                 switch (tpl.value) {
13450                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13451                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13452                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13453                 }
13454                 break;
13455             
13456             case 'exec':
13457                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13458                 break;
13459             
13460             case 'if':     
13461                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13462                 break;
13463             
13464             case 'name':
13465                 tpl.id  = tpl.value; // replace non characters???
13466                 break;
13467             
13468         }
13469         
13470         
13471         this.tpls.push(tpl);
13472         
13473         
13474         
13475     },
13476     
13477     
13478     
13479     
13480     /**
13481      * Compile a segment of the template into a 'sub-template'
13482      *
13483      * 
13484      * 
13485      *
13486      */
13487     compileTpl : function(tpl)
13488     {
13489         var fm = Roo.util.Format;
13490         var useF = this.disableFormats !== true;
13491         
13492         var sep = Roo.isGecko ? "+\n" : ",\n";
13493         
13494         var undef = function(str) {
13495             Roo.debug && Roo.log("Property not found :"  + str);
13496             return '';
13497         };
13498           
13499         //Roo.log(tpl.body);
13500         
13501         
13502         
13503         var fn = function(m, lbrace, name, format, args)
13504         {
13505             //Roo.log("ARGS");
13506             //Roo.log(arguments);
13507             args = args ? args.replace(/\\'/g,"'") : args;
13508             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13509             if (typeof(format) == 'undefined') {
13510                 format =  'htmlEncode'; 
13511             }
13512             if (format == 'raw' ) {
13513                 format = false;
13514             }
13515             
13516             if(name.substr(0, 6) == 'domtpl'){
13517                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13518             }
13519             
13520             // build an array of options to determine if value is undefined..
13521             
13522             // basically get 'xxxx.yyyy' then do
13523             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13524             //    (function () { Roo.log("Property not found"); return ''; })() :
13525             //    ......
13526             
13527             var udef_ar = [];
13528             var lookfor = '';
13529             Roo.each(name.split('.'), function(st) {
13530                 lookfor += (lookfor.length ? '.': '') + st;
13531                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13532             });
13533             
13534             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13535             
13536             
13537             if(format && useF){
13538                 
13539                 args = args ? ',' + args : "";
13540                  
13541                 if(format.substr(0, 5) != "this."){
13542                     format = "fm." + format + '(';
13543                 }else{
13544                     format = 'this.call("'+ format.substr(5) + '", ';
13545                     args = ", values";
13546                 }
13547                 
13548                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13549             }
13550              
13551             if (args && args.length) {
13552                 // called with xxyx.yuu:(test,test)
13553                 // change to ()
13554                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13555             }
13556             // raw.. - :raw modifier..
13557             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13558             
13559         };
13560         var body;
13561         // branched to use + in gecko and [].join() in others
13562         if(Roo.isGecko){
13563             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13564                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13565                     "';};};";
13566         }else{
13567             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13568             body.push(tpl.body.replace(/(\r\n|\n)/g,
13569                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13570             body.push("'].join('');};};");
13571             body = body.join('');
13572         }
13573         
13574         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13575        
13576         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13577         eval(body);
13578         
13579         return this;
13580     },
13581      
13582     /**
13583      * same as applyTemplate, except it's done to one of the subTemplates
13584      * when using named templates, you can do:
13585      *
13586      * var str = pl.applySubTemplate('your-name', values);
13587      *
13588      * 
13589      * @param {Number} id of the template
13590      * @param {Object} values to apply to template
13591      * @param {Object} parent (normaly the instance of this object)
13592      */
13593     applySubTemplate : function(id, values, parent)
13594     {
13595         
13596         
13597         var t = this.tpls[id];
13598         
13599         
13600         try { 
13601             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13602                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13603                 return '';
13604             }
13605         } catch(e) {
13606             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13607             Roo.log(values);
13608           
13609             return '';
13610         }
13611         try { 
13612             
13613             if(t.execCall && t.execCall.call(this, values, parent)){
13614                 return '';
13615             }
13616         } catch(e) {
13617             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13618             Roo.log(values);
13619             return '';
13620         }
13621         
13622         try {
13623             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13624             parent = t.target ? values : parent;
13625             if(t.forCall && vs instanceof Array){
13626                 var buf = [];
13627                 for(var i = 0, len = vs.length; i < len; i++){
13628                     try {
13629                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13630                     } catch (e) {
13631                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13632                         Roo.log(e.body);
13633                         //Roo.log(t.compiled);
13634                         Roo.log(vs[i]);
13635                     }   
13636                 }
13637                 return buf.join('');
13638             }
13639         } catch (e) {
13640             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13641             Roo.log(values);
13642             return '';
13643         }
13644         try {
13645             return t.compiled.call(this, vs, parent);
13646         } catch (e) {
13647             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13648             Roo.log(e.body);
13649             //Roo.log(t.compiled);
13650             Roo.log(values);
13651             return '';
13652         }
13653     },
13654
13655    
13656
13657     applyTemplate : function(values){
13658         return this.master.compiled.call(this, values, {});
13659         //var s = this.subs;
13660     },
13661
13662     apply : function(){
13663         return this.applyTemplate.apply(this, arguments);
13664     }
13665
13666  });
13667
13668 Roo.DomTemplate.from = function(el){
13669     el = Roo.getDom(el);
13670     return new Roo.Domtemplate(el.value || el.innerHTML);
13671 };/*
13672  * Based on:
13673  * Ext JS Library 1.1.1
13674  * Copyright(c) 2006-2007, Ext JS, LLC.
13675  *
13676  * Originally Released Under LGPL - original licence link has changed is not relivant.
13677  *
13678  * Fork - LGPL
13679  * <script type="text/javascript">
13680  */
13681
13682 /**
13683  * @class Roo.util.DelayedTask
13684  * Provides a convenient method of performing setTimeout where a new
13685  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13686  * You can use this class to buffer
13687  * the keypress events for a certain number of milliseconds, and perform only if they stop
13688  * for that amount of time.
13689  * @constructor The parameters to this constructor serve as defaults and are not required.
13690  * @param {Function} fn (optional) The default function to timeout
13691  * @param {Object} scope (optional) The default scope of that timeout
13692  * @param {Array} args (optional) The default Array of arguments
13693  */
13694 Roo.util.DelayedTask = function(fn, scope, args){
13695     var id = null, d, t;
13696
13697     var call = function(){
13698         var now = new Date().getTime();
13699         if(now - t >= d){
13700             clearInterval(id);
13701             id = null;
13702             fn.apply(scope, args || []);
13703         }
13704     };
13705     /**
13706      * Cancels any pending timeout and queues a new one
13707      * @param {Number} delay The milliseconds to delay
13708      * @param {Function} newFn (optional) Overrides function passed to constructor
13709      * @param {Object} newScope (optional) Overrides scope passed to constructor
13710      * @param {Array} newArgs (optional) Overrides args passed to constructor
13711      */
13712     this.delay = function(delay, newFn, newScope, newArgs){
13713         if(id && delay != d){
13714             this.cancel();
13715         }
13716         d = delay;
13717         t = new Date().getTime();
13718         fn = newFn || fn;
13719         scope = newScope || scope;
13720         args = newArgs || args;
13721         if(!id){
13722             id = setInterval(call, d);
13723         }
13724     };
13725
13726     /**
13727      * Cancel the last queued timeout
13728      */
13729     this.cancel = function(){
13730         if(id){
13731             clearInterval(id);
13732             id = null;
13733         }
13734     };
13735 };/*
13736  * Based on:
13737  * Ext JS Library 1.1.1
13738  * Copyright(c) 2006-2007, Ext JS, LLC.
13739  *
13740  * Originally Released Under LGPL - original licence link has changed is not relivant.
13741  *
13742  * Fork - LGPL
13743  * <script type="text/javascript">
13744  */
13745 /**
13746  * @class Roo.util.TaskRunner
13747  * Manage background tasks - not sure why this is better that setInterval?
13748  * @static
13749  *
13750  */
13751  
13752 Roo.util.TaskRunner = function(interval){
13753     interval = interval || 10;
13754     var tasks = [], removeQueue = [];
13755     var id = 0;
13756     var running = false;
13757
13758     var stopThread = function(){
13759         running = false;
13760         clearInterval(id);
13761         id = 0;
13762     };
13763
13764     var startThread = function(){
13765         if(!running){
13766             running = true;
13767             id = setInterval(runTasks, interval);
13768         }
13769     };
13770
13771     var removeTask = function(task){
13772         removeQueue.push(task);
13773         if(task.onStop){
13774             task.onStop();
13775         }
13776     };
13777
13778     var runTasks = function(){
13779         if(removeQueue.length > 0){
13780             for(var i = 0, len = removeQueue.length; i < len; i++){
13781                 tasks.remove(removeQueue[i]);
13782             }
13783             removeQueue = [];
13784             if(tasks.length < 1){
13785                 stopThread();
13786                 return;
13787             }
13788         }
13789         var now = new Date().getTime();
13790         for(var i = 0, len = tasks.length; i < len; ++i){
13791             var t = tasks[i];
13792             var itime = now - t.taskRunTime;
13793             if(t.interval <= itime){
13794                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13795                 t.taskRunTime = now;
13796                 if(rt === false || t.taskRunCount === t.repeat){
13797                     removeTask(t);
13798                     return;
13799                 }
13800             }
13801             if(t.duration && t.duration <= (now - t.taskStartTime)){
13802                 removeTask(t);
13803             }
13804         }
13805     };
13806
13807     /**
13808      * Queues a new task.
13809      * @param {Object} task
13810      *
13811      * Task property : interval = how frequent to run.
13812      * Task object should implement
13813      * function run()
13814      * Task object may implement
13815      * function onStop()
13816      */
13817     this.start = function(task){
13818         tasks.push(task);
13819         task.taskStartTime = new Date().getTime();
13820         task.taskRunTime = 0;
13821         task.taskRunCount = 0;
13822         startThread();
13823         return task;
13824     };
13825     /**
13826      * Stop  new task.
13827      * @param {Object} task
13828      */
13829     this.stop = function(task){
13830         removeTask(task);
13831         return task;
13832     };
13833     /**
13834      * Stop all Tasks
13835      */
13836     this.stopAll = function(){
13837         stopThread();
13838         for(var i = 0, len = tasks.length; i < len; i++){
13839             if(tasks[i].onStop){
13840                 tasks[i].onStop();
13841             }
13842         }
13843         tasks = [];
13844         removeQueue = [];
13845     };
13846 };
13847
13848 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13849  * Based on:
13850  * Ext JS Library 1.1.1
13851  * Copyright(c) 2006-2007, Ext JS, LLC.
13852  *
13853  * Originally Released Under LGPL - original licence link has changed is not relivant.
13854  *
13855  * Fork - LGPL
13856  * <script type="text/javascript">
13857  */
13858
13859  
13860 /**
13861  * @class Roo.util.MixedCollection
13862  * @extends Roo.util.Observable
13863  * A Collection class that maintains both numeric indexes and keys and exposes events.
13864  * @constructor
13865  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13866  * collection (defaults to false)
13867  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13868  * and return the key value for that item.  This is used when available to look up the key on items that
13869  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13870  * equivalent to providing an implementation for the {@link #getKey} method.
13871  */
13872 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13873     this.items = [];
13874     this.map = {};
13875     this.keys = [];
13876     this.length = 0;
13877     this.addEvents({
13878         /**
13879          * @event clear
13880          * Fires when the collection is cleared.
13881          */
13882         "clear" : true,
13883         /**
13884          * @event add
13885          * Fires when an item is added to the collection.
13886          * @param {Number} index The index at which the item was added.
13887          * @param {Object} o The item added.
13888          * @param {String} key The key associated with the added item.
13889          */
13890         "add" : true,
13891         /**
13892          * @event replace
13893          * Fires when an item is replaced in the collection.
13894          * @param {String} key he key associated with the new added.
13895          * @param {Object} old The item being replaced.
13896          * @param {Object} new The new item.
13897          */
13898         "replace" : true,
13899         /**
13900          * @event remove
13901          * Fires when an item is removed from the collection.
13902          * @param {Object} o The item being removed.
13903          * @param {String} key (optional) The key associated with the removed item.
13904          */
13905         "remove" : true,
13906         "sort" : true
13907     });
13908     this.allowFunctions = allowFunctions === true;
13909     if(keyFn){
13910         this.getKey = keyFn;
13911     }
13912     Roo.util.MixedCollection.superclass.constructor.call(this);
13913 };
13914
13915 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13916     allowFunctions : false,
13917     
13918 /**
13919  * Adds an item to the collection.
13920  * @param {String} key The key to associate with the item
13921  * @param {Object} o The item to add.
13922  * @return {Object} The item added.
13923  */
13924     add : function(key, o){
13925         if(arguments.length == 1){
13926             o = arguments[0];
13927             key = this.getKey(o);
13928         }
13929         if(typeof key == "undefined" || key === null){
13930             this.length++;
13931             this.items.push(o);
13932             this.keys.push(null);
13933         }else{
13934             var old = this.map[key];
13935             if(old){
13936                 return this.replace(key, o);
13937             }
13938             this.length++;
13939             this.items.push(o);
13940             this.map[key] = o;
13941             this.keys.push(key);
13942         }
13943         this.fireEvent("add", this.length-1, o, key);
13944         return o;
13945     },
13946        
13947 /**
13948   * MixedCollection has a generic way to fetch keys if you implement getKey.
13949 <pre><code>
13950 // normal way
13951 var mc = new Roo.util.MixedCollection();
13952 mc.add(someEl.dom.id, someEl);
13953 mc.add(otherEl.dom.id, otherEl);
13954 //and so on
13955
13956 // using getKey
13957 var mc = new Roo.util.MixedCollection();
13958 mc.getKey = function(el){
13959    return el.dom.id;
13960 };
13961 mc.add(someEl);
13962 mc.add(otherEl);
13963
13964 // or via the constructor
13965 var mc = new Roo.util.MixedCollection(false, function(el){
13966    return el.dom.id;
13967 });
13968 mc.add(someEl);
13969 mc.add(otherEl);
13970 </code></pre>
13971  * @param o {Object} The item for which to find the key.
13972  * @return {Object} The key for the passed item.
13973  */
13974     getKey : function(o){
13975          return o.id; 
13976     },
13977    
13978 /**
13979  * Replaces an item in the collection.
13980  * @param {String} key The key associated with the item to replace, or the item to replace.
13981  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13982  * @return {Object}  The new item.
13983  */
13984     replace : function(key, o){
13985         if(arguments.length == 1){
13986             o = arguments[0];
13987             key = this.getKey(o);
13988         }
13989         var old = this.item(key);
13990         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13991              return this.add(key, o);
13992         }
13993         var index = this.indexOfKey(key);
13994         this.items[index] = o;
13995         this.map[key] = o;
13996         this.fireEvent("replace", key, old, o);
13997         return o;
13998     },
13999    
14000 /**
14001  * Adds all elements of an Array or an Object to the collection.
14002  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14003  * an Array of values, each of which are added to the collection.
14004  */
14005     addAll : function(objs){
14006         if(arguments.length > 1 || objs instanceof Array){
14007             var args = arguments.length > 1 ? arguments : objs;
14008             for(var i = 0, len = args.length; i < len; i++){
14009                 this.add(args[i]);
14010             }
14011         }else{
14012             for(var key in objs){
14013                 if(this.allowFunctions || typeof objs[key] != "function"){
14014                     this.add(key, objs[key]);
14015                 }
14016             }
14017         }
14018     },
14019    
14020 /**
14021  * Executes the specified function once for every item in the collection, passing each
14022  * item as the first and only parameter. returning false from the function will stop the iteration.
14023  * @param {Function} fn The function to execute for each item.
14024  * @param {Object} scope (optional) The scope in which to execute the function.
14025  */
14026     each : function(fn, scope){
14027         var items = [].concat(this.items); // each safe for removal
14028         for(var i = 0, len = items.length; i < len; i++){
14029             if(fn.call(scope || items[i], items[i], i, len) === false){
14030                 break;
14031             }
14032         }
14033     },
14034    
14035 /**
14036  * Executes the specified function once for every key in the collection, passing each
14037  * key, and its associated item as the first two parameters.
14038  * @param {Function} fn The function to execute for each item.
14039  * @param {Object} scope (optional) The scope in which to execute the function.
14040  */
14041     eachKey : function(fn, scope){
14042         for(var i = 0, len = this.keys.length; i < len; i++){
14043             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14044         }
14045     },
14046    
14047 /**
14048  * Returns the first item in the collection which elicits a true return value from the
14049  * passed selection function.
14050  * @param {Function} fn The selection function to execute for each item.
14051  * @param {Object} scope (optional) The scope in which to execute the function.
14052  * @return {Object} The first item in the collection which returned true from the selection function.
14053  */
14054     find : function(fn, scope){
14055         for(var i = 0, len = this.items.length; i < len; i++){
14056             if(fn.call(scope || window, this.items[i], this.keys[i])){
14057                 return this.items[i];
14058             }
14059         }
14060         return null;
14061     },
14062    
14063 /**
14064  * Inserts an item at the specified index in the collection.
14065  * @param {Number} index The index to insert the item at.
14066  * @param {String} key The key to associate with the new item, or the item itself.
14067  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14068  * @return {Object} The item inserted.
14069  */
14070     insert : function(index, key, o){
14071         if(arguments.length == 2){
14072             o = arguments[1];
14073             key = this.getKey(o);
14074         }
14075         if(index >= this.length){
14076             return this.add(key, o);
14077         }
14078         this.length++;
14079         this.items.splice(index, 0, o);
14080         if(typeof key != "undefined" && key != null){
14081             this.map[key] = o;
14082         }
14083         this.keys.splice(index, 0, key);
14084         this.fireEvent("add", index, o, key);
14085         return o;
14086     },
14087    
14088 /**
14089  * Removed an item from the collection.
14090  * @param {Object} o The item to remove.
14091  * @return {Object} The item removed.
14092  */
14093     remove : function(o){
14094         return this.removeAt(this.indexOf(o));
14095     },
14096    
14097 /**
14098  * Remove an item from a specified index in the collection.
14099  * @param {Number} index The index within the collection of the item to remove.
14100  */
14101     removeAt : function(index){
14102         if(index < this.length && index >= 0){
14103             this.length--;
14104             var o = this.items[index];
14105             this.items.splice(index, 1);
14106             var key = this.keys[index];
14107             if(typeof key != "undefined"){
14108                 delete this.map[key];
14109             }
14110             this.keys.splice(index, 1);
14111             this.fireEvent("remove", o, key);
14112         }
14113     },
14114    
14115 /**
14116  * Removed an item associated with the passed key fom the collection.
14117  * @param {String} key The key of the item to remove.
14118  */
14119     removeKey : function(key){
14120         return this.removeAt(this.indexOfKey(key));
14121     },
14122    
14123 /**
14124  * Returns the number of items in the collection.
14125  * @return {Number} the number of items in the collection.
14126  */
14127     getCount : function(){
14128         return this.length; 
14129     },
14130    
14131 /**
14132  * Returns index within the collection of the passed Object.
14133  * @param {Object} o The item to find the index of.
14134  * @return {Number} index of the item.
14135  */
14136     indexOf : function(o){
14137         if(!this.items.indexOf){
14138             for(var i = 0, len = this.items.length; i < len; i++){
14139                 if(this.items[i] == o) {
14140                     return i;
14141                 }
14142             }
14143             return -1;
14144         }else{
14145             return this.items.indexOf(o);
14146         }
14147     },
14148    
14149 /**
14150  * Returns index within the collection of the passed key.
14151  * @param {String} key The key to find the index of.
14152  * @return {Number} index of the key.
14153  */
14154     indexOfKey : function(key){
14155         if(!this.keys.indexOf){
14156             for(var i = 0, len = this.keys.length; i < len; i++){
14157                 if(this.keys[i] == key) {
14158                     return i;
14159                 }
14160             }
14161             return -1;
14162         }else{
14163             return this.keys.indexOf(key);
14164         }
14165     },
14166    
14167 /**
14168  * Returns the item associated with the passed key OR index. Key has priority over index.
14169  * @param {String/Number} key The key or index of the item.
14170  * @return {Object} The item associated with the passed key.
14171  */
14172     item : function(key){
14173         if (key === 'length') {
14174             return null;
14175         }
14176         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14177         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14178     },
14179     
14180 /**
14181  * Returns the item at the specified index.
14182  * @param {Number} index The index of the item.
14183  * @return {Object}
14184  */
14185     itemAt : function(index){
14186         return this.items[index];
14187     },
14188     
14189 /**
14190  * Returns the item associated with the passed key.
14191  * @param {String/Number} key The key of the item.
14192  * @return {Object} The item associated with the passed key.
14193  */
14194     key : function(key){
14195         return this.map[key];
14196     },
14197    
14198 /**
14199  * Returns true if the collection contains the passed Object as an item.
14200  * @param {Object} o  The Object to look for in the collection.
14201  * @return {Boolean} True if the collection contains the Object as an item.
14202  */
14203     contains : function(o){
14204         return this.indexOf(o) != -1;
14205     },
14206    
14207 /**
14208  * Returns true if the collection contains the passed Object as a key.
14209  * @param {String} key The key to look for in the collection.
14210  * @return {Boolean} True if the collection contains the Object as a key.
14211  */
14212     containsKey : function(key){
14213         return typeof this.map[key] != "undefined";
14214     },
14215    
14216 /**
14217  * Removes all items from the collection.
14218  */
14219     clear : function(){
14220         this.length = 0;
14221         this.items = [];
14222         this.keys = [];
14223         this.map = {};
14224         this.fireEvent("clear");
14225     },
14226    
14227 /**
14228  * Returns the first item in the collection.
14229  * @return {Object} the first item in the collection..
14230  */
14231     first : function(){
14232         return this.items[0]; 
14233     },
14234    
14235 /**
14236  * Returns the last item in the collection.
14237  * @return {Object} the last item in the collection..
14238  */
14239     last : function(){
14240         return this.items[this.length-1];   
14241     },
14242     
14243     _sort : function(property, dir, fn){
14244         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14245         fn = fn || function(a, b){
14246             return a-b;
14247         };
14248         var c = [], k = this.keys, items = this.items;
14249         for(var i = 0, len = items.length; i < len; i++){
14250             c[c.length] = {key: k[i], value: items[i], index: i};
14251         }
14252         c.sort(function(a, b){
14253             var v = fn(a[property], b[property]) * dsc;
14254             if(v == 0){
14255                 v = (a.index < b.index ? -1 : 1);
14256             }
14257             return v;
14258         });
14259         for(var i = 0, len = c.length; i < len; i++){
14260             items[i] = c[i].value;
14261             k[i] = c[i].key;
14262         }
14263         this.fireEvent("sort", this);
14264     },
14265     
14266     /**
14267      * Sorts this collection with the passed comparison function
14268      * @param {String} direction (optional) "ASC" or "DESC"
14269      * @param {Function} fn (optional) comparison function
14270      */
14271     sort : function(dir, fn){
14272         this._sort("value", dir, fn);
14273     },
14274     
14275     /**
14276      * Sorts this collection by keys
14277      * @param {String} direction (optional) "ASC" or "DESC"
14278      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14279      */
14280     keySort : function(dir, fn){
14281         this._sort("key", dir, fn || function(a, b){
14282             return String(a).toUpperCase()-String(b).toUpperCase();
14283         });
14284     },
14285     
14286     /**
14287      * Returns a range of items in this collection
14288      * @param {Number} startIndex (optional) defaults to 0
14289      * @param {Number} endIndex (optional) default to the last item
14290      * @return {Array} An array of items
14291      */
14292     getRange : function(start, end){
14293         var items = this.items;
14294         if(items.length < 1){
14295             return [];
14296         }
14297         start = start || 0;
14298         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14299         var r = [];
14300         if(start <= end){
14301             for(var i = start; i <= end; i++) {
14302                     r[r.length] = items[i];
14303             }
14304         }else{
14305             for(var i = start; i >= end; i--) {
14306                     r[r.length] = items[i];
14307             }
14308         }
14309         return r;
14310     },
14311         
14312     /**
14313      * Filter the <i>objects</i> in this collection by a specific property. 
14314      * Returns a new collection that has been filtered.
14315      * @param {String} property A property on your objects
14316      * @param {String/RegExp} value Either string that the property values 
14317      * should start with or a RegExp to test against the property
14318      * @return {MixedCollection} The new filtered collection
14319      */
14320     filter : function(property, value){
14321         if(!value.exec){ // not a regex
14322             value = String(value);
14323             if(value.length == 0){
14324                 return this.clone();
14325             }
14326             value = new RegExp("^" + Roo.escapeRe(value), "i");
14327         }
14328         return this.filterBy(function(o){
14329             return o && value.test(o[property]);
14330         });
14331         },
14332     
14333     /**
14334      * Filter by a function. * Returns a new collection that has been filtered.
14335      * The passed function will be called with each 
14336      * object in the collection. If the function returns true, the value is included 
14337      * otherwise it is filtered.
14338      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14339      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14340      * @return {MixedCollection} The new filtered collection
14341      */
14342     filterBy : function(fn, scope){
14343         var r = new Roo.util.MixedCollection();
14344         r.getKey = this.getKey;
14345         var k = this.keys, it = this.items;
14346         for(var i = 0, len = it.length; i < len; i++){
14347             if(fn.call(scope||this, it[i], k[i])){
14348                                 r.add(k[i], it[i]);
14349                         }
14350         }
14351         return r;
14352     },
14353     
14354     /**
14355      * Creates a duplicate of this collection
14356      * @return {MixedCollection}
14357      */
14358     clone : function(){
14359         var r = new Roo.util.MixedCollection();
14360         var k = this.keys, it = this.items;
14361         for(var i = 0, len = it.length; i < len; i++){
14362             r.add(k[i], it[i]);
14363         }
14364         r.getKey = this.getKey;
14365         return r;
14366     }
14367 });
14368 /**
14369  * Returns the item associated with the passed key or index.
14370  * @method
14371  * @param {String/Number} key The key or index of the item.
14372  * @return {Object} The item associated with the passed key.
14373  */
14374 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14375  * Based on:
14376  * Ext JS Library 1.1.1
14377  * Copyright(c) 2006-2007, Ext JS, LLC.
14378  *
14379  * Originally Released Under LGPL - original licence link has changed is not relivant.
14380  *
14381  * Fork - LGPL
14382  * <script type="text/javascript">
14383  */
14384 /**
14385  * @class Roo.util.JSON
14386  * Modified version of Douglas Crockford"s json.js that doesn"t
14387  * mess with the Object prototype 
14388  * http://www.json.org/js.html
14389  * @static
14390  */
14391 Roo.util.JSON = new (function(){
14392     var useHasOwn = {}.hasOwnProperty ? true : false;
14393     
14394     // crashes Safari in some instances
14395     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14396     
14397     var pad = function(n) {
14398         return n < 10 ? "0" + n : n;
14399     };
14400     
14401     var m = {
14402         "\b": '\\b',
14403         "\t": '\\t',
14404         "\n": '\\n',
14405         "\f": '\\f',
14406         "\r": '\\r',
14407         '"' : '\\"',
14408         "\\": '\\\\'
14409     };
14410
14411     var encodeString = function(s){
14412         if (/["\\\x00-\x1f]/.test(s)) {
14413             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14414                 var c = m[b];
14415                 if(c){
14416                     return c;
14417                 }
14418                 c = b.charCodeAt();
14419                 return "\\u00" +
14420                     Math.floor(c / 16).toString(16) +
14421                     (c % 16).toString(16);
14422             }) + '"';
14423         }
14424         return '"' + s + '"';
14425     };
14426     
14427     var encodeArray = function(o){
14428         var a = ["["], b, i, l = o.length, v;
14429             for (i = 0; i < l; i += 1) {
14430                 v = o[i];
14431                 switch (typeof v) {
14432                     case "undefined":
14433                     case "function":
14434                     case "unknown":
14435                         break;
14436                     default:
14437                         if (b) {
14438                             a.push(',');
14439                         }
14440                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14441                         b = true;
14442                 }
14443             }
14444             a.push("]");
14445             return a.join("");
14446     };
14447     
14448     var encodeDate = function(o){
14449         return '"' + o.getFullYear() + "-" +
14450                 pad(o.getMonth() + 1) + "-" +
14451                 pad(o.getDate()) + "T" +
14452                 pad(o.getHours()) + ":" +
14453                 pad(o.getMinutes()) + ":" +
14454                 pad(o.getSeconds()) + '"';
14455     };
14456     
14457     /**
14458      * Encodes an Object, Array or other value
14459      * @param {Mixed} o The variable to encode
14460      * @return {String} The JSON string
14461      */
14462     this.encode = function(o)
14463     {
14464         // should this be extended to fully wrap stringify..
14465         
14466         if(typeof o == "undefined" || o === null){
14467             return "null";
14468         }else if(o instanceof Array){
14469             return encodeArray(o);
14470         }else if(o instanceof Date){
14471             return encodeDate(o);
14472         }else if(typeof o == "string"){
14473             return encodeString(o);
14474         }else if(typeof o == "number"){
14475             return isFinite(o) ? String(o) : "null";
14476         }else if(typeof o == "boolean"){
14477             return String(o);
14478         }else {
14479             var a = ["{"], b, i, v;
14480             for (i in o) {
14481                 if(!useHasOwn || o.hasOwnProperty(i)) {
14482                     v = o[i];
14483                     switch (typeof v) {
14484                     case "undefined":
14485                     case "function":
14486                     case "unknown":
14487                         break;
14488                     default:
14489                         if(b){
14490                             a.push(',');
14491                         }
14492                         a.push(this.encode(i), ":",
14493                                 v === null ? "null" : this.encode(v));
14494                         b = true;
14495                     }
14496                 }
14497             }
14498             a.push("}");
14499             return a.join("");
14500         }
14501     };
14502     
14503     /**
14504      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14505      * @param {String} json The JSON string
14506      * @return {Object} The resulting object
14507      */
14508     this.decode = function(json){
14509         
14510         return  /** eval:var:json */ eval("(" + json + ')');
14511     };
14512 })();
14513 /** 
14514  * Shorthand for {@link Roo.util.JSON#encode}
14515  * @member Roo encode 
14516  * @method */
14517 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14518 /** 
14519  * Shorthand for {@link Roo.util.JSON#decode}
14520  * @member Roo decode 
14521  * @method */
14522 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14523 /*
14524  * Based on:
14525  * Ext JS Library 1.1.1
14526  * Copyright(c) 2006-2007, Ext JS, LLC.
14527  *
14528  * Originally Released Under LGPL - original licence link has changed is not relivant.
14529  *
14530  * Fork - LGPL
14531  * <script type="text/javascript">
14532  */
14533  
14534 /**
14535  * @class Roo.util.Format
14536  * Reusable data formatting functions
14537  * @static
14538  */
14539 Roo.util.Format = function(){
14540     var trimRe = /^\s+|\s+$/g;
14541     return {
14542         /**
14543          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14544          * @param {String} value The string to truncate
14545          * @param {Number} length The maximum length to allow before truncating
14546          * @return {String} The converted text
14547          */
14548         ellipsis : function(value, len){
14549             if(value && value.length > len){
14550                 return value.substr(0, len-3)+"...";
14551             }
14552             return value;
14553         },
14554
14555         /**
14556          * Checks a reference and converts it to empty string if it is undefined
14557          * @param {Mixed} value Reference to check
14558          * @return {Mixed} Empty string if converted, otherwise the original value
14559          */
14560         undef : function(value){
14561             return typeof value != "undefined" ? value : "";
14562         },
14563
14564         /**
14565          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14566          * @param {String} value The string to encode
14567          * @return {String} The encoded text
14568          */
14569         htmlEncode : function(value){
14570             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14571         },
14572
14573         /**
14574          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14575          * @param {String} value The string to decode
14576          * @return {String} The decoded text
14577          */
14578         htmlDecode : function(value){
14579             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14580         },
14581
14582         /**
14583          * Trims any whitespace from either side of a string
14584          * @param {String} value The text to trim
14585          * @return {String} The trimmed text
14586          */
14587         trim : function(value){
14588             return String(value).replace(trimRe, "");
14589         },
14590
14591         /**
14592          * Returns a substring from within an original string
14593          * @param {String} value The original text
14594          * @param {Number} start The start index of the substring
14595          * @param {Number} length The length of the substring
14596          * @return {String} The substring
14597          */
14598         substr : function(value, start, length){
14599             return String(value).substr(start, length);
14600         },
14601
14602         /**
14603          * Converts a string to all lower case letters
14604          * @param {String} value The text to convert
14605          * @return {String} The converted text
14606          */
14607         lowercase : function(value){
14608             return String(value).toLowerCase();
14609         },
14610
14611         /**
14612          * Converts a string to all upper case letters
14613          * @param {String} value The text to convert
14614          * @return {String} The converted text
14615          */
14616         uppercase : function(value){
14617             return String(value).toUpperCase();
14618         },
14619
14620         /**
14621          * Converts the first character only of a string to upper case
14622          * @param {String} value The text to convert
14623          * @return {String} The converted text
14624          */
14625         capitalize : function(value){
14626             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14627         },
14628
14629         // private
14630         call : function(value, fn){
14631             if(arguments.length > 2){
14632                 var args = Array.prototype.slice.call(arguments, 2);
14633                 args.unshift(value);
14634                  
14635                 return /** eval:var:value */  eval(fn).apply(window, args);
14636             }else{
14637                 /** eval:var:value */
14638                 return /** eval:var:value */ eval(fn).call(window, value);
14639             }
14640         },
14641
14642        
14643         /**
14644          * safer version of Math.toFixed..??/
14645          * @param {Number/String} value The numeric value to format
14646          * @param {Number/String} value Decimal places 
14647          * @return {String} The formatted currency string
14648          */
14649         toFixed : function(v, n)
14650         {
14651             // why not use to fixed - precision is buggered???
14652             if (!n) {
14653                 return Math.round(v-0);
14654             }
14655             var fact = Math.pow(10,n+1);
14656             v = (Math.round((v-0)*fact))/fact;
14657             var z = (''+fact).substring(2);
14658             if (v == Math.floor(v)) {
14659                 return Math.floor(v) + '.' + z;
14660             }
14661             
14662             // now just padd decimals..
14663             var ps = String(v).split('.');
14664             var fd = (ps[1] + z);
14665             var r = fd.substring(0,n); 
14666             var rm = fd.substring(n); 
14667             if (rm < 5) {
14668                 return ps[0] + '.' + r;
14669             }
14670             r*=1; // turn it into a number;
14671             r++;
14672             if (String(r).length != n) {
14673                 ps[0]*=1;
14674                 ps[0]++;
14675                 r = String(r).substring(1); // chop the end off.
14676             }
14677             
14678             return ps[0] + '.' + r;
14679              
14680         },
14681         
14682         /**
14683          * Format a number as US currency
14684          * @param {Number/String} value The numeric value to format
14685          * @return {String} The formatted currency string
14686          */
14687         usMoney : function(v){
14688             return '$' + Roo.util.Format.number(v);
14689         },
14690         
14691         /**
14692          * Format a number
14693          * eventually this should probably emulate php's number_format
14694          * @param {Number/String} value The numeric value to format
14695          * @param {Number} decimals number of decimal places
14696          * @param {String} delimiter for thousands (default comma)
14697          * @return {String} The formatted currency string
14698          */
14699         number : function(v, decimals, thousandsDelimiter)
14700         {
14701             // multiply and round.
14702             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14703             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14704             
14705             var mul = Math.pow(10, decimals);
14706             var zero = String(mul).substring(1);
14707             v = (Math.round((v-0)*mul))/mul;
14708             
14709             // if it's '0' number.. then
14710             
14711             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14712             v = String(v);
14713             var ps = v.split('.');
14714             var whole = ps[0];
14715             
14716             var r = /(\d+)(\d{3})/;
14717             // add comma's
14718             
14719             if(thousandsDelimiter.length != 0) {
14720                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14721             } 
14722             
14723             var sub = ps[1] ?
14724                     // has decimals..
14725                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14726                     // does not have decimals
14727                     (decimals ? ('.' + zero) : '');
14728             
14729             
14730             return whole + sub ;
14731         },
14732         
14733         /**
14734          * Parse a value into a formatted date using the specified format pattern.
14735          * @param {Mixed} value The value to format
14736          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14737          * @return {String} The formatted date string
14738          */
14739         date : function(v, format){
14740             if(!v){
14741                 return "";
14742             }
14743             if(!(v instanceof Date)){
14744                 v = new Date(Date.parse(v));
14745             }
14746             return v.dateFormat(format || Roo.util.Format.defaults.date);
14747         },
14748
14749         /**
14750          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14751          * @param {String} format Any valid date format string
14752          * @return {Function} The date formatting function
14753          */
14754         dateRenderer : function(format){
14755             return function(v){
14756                 return Roo.util.Format.date(v, format);  
14757             };
14758         },
14759
14760         // private
14761         stripTagsRE : /<\/?[^>]+>/gi,
14762         
14763         /**
14764          * Strips all HTML tags
14765          * @param {Mixed} value The text from which to strip tags
14766          * @return {String} The stripped text
14767          */
14768         stripTags : function(v){
14769             return !v ? v : String(v).replace(this.stripTagsRE, "");
14770         },
14771         
14772         /**
14773          * Size in Mb,Gb etc.
14774          * @param {Number} value The number to be formated
14775          * @param {number} decimals how many decimal places
14776          * @return {String} the formated string
14777          */
14778         size : function(value, decimals)
14779         {
14780             var sizes = ['b', 'k', 'M', 'G', 'T'];
14781             if (value == 0) {
14782                 return 0;
14783             }
14784             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14785             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14786         }
14787         
14788         
14789         
14790     };
14791 }();
14792 Roo.util.Format.defaults = {
14793     date : 'd/M/Y'
14794 };/*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804
14805
14806  
14807
14808 /**
14809  * @class Roo.MasterTemplate
14810  * @extends Roo.Template
14811  * Provides a template that can have child templates. The syntax is:
14812 <pre><code>
14813 var t = new Roo.MasterTemplate(
14814         '&lt;select name="{name}"&gt;',
14815                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14816         '&lt;/select&gt;'
14817 );
14818 t.add('options', {value: 'foo', text: 'bar'});
14819 // or you can add multiple child elements in one shot
14820 t.addAll('options', [
14821     {value: 'foo', text: 'bar'},
14822     {value: 'foo2', text: 'bar2'},
14823     {value: 'foo3', text: 'bar3'}
14824 ]);
14825 // then append, applying the master template values
14826 t.append('my-form', {name: 'my-select'});
14827 </code></pre>
14828 * A name attribute for the child template is not required if you have only one child
14829 * template or you want to refer to them by index.
14830  */
14831 Roo.MasterTemplate = function(){
14832     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14833     this.originalHtml = this.html;
14834     var st = {};
14835     var m, re = this.subTemplateRe;
14836     re.lastIndex = 0;
14837     var subIndex = 0;
14838     while(m = re.exec(this.html)){
14839         var name = m[1], content = m[2];
14840         st[subIndex] = {
14841             name: name,
14842             index: subIndex,
14843             buffer: [],
14844             tpl : new Roo.Template(content)
14845         };
14846         if(name){
14847             st[name] = st[subIndex];
14848         }
14849         st[subIndex].tpl.compile();
14850         st[subIndex].tpl.call = this.call.createDelegate(this);
14851         subIndex++;
14852     }
14853     this.subCount = subIndex;
14854     this.subs = st;
14855 };
14856 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14857     /**
14858     * The regular expression used to match sub templates
14859     * @type RegExp
14860     * @property
14861     */
14862     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14863
14864     /**
14865      * Applies the passed values to a child template.
14866      * @param {String/Number} name (optional) The name or index of the child template
14867      * @param {Array/Object} values The values to be applied to the template
14868      * @return {MasterTemplate} this
14869      */
14870      add : function(name, values){
14871         if(arguments.length == 1){
14872             values = arguments[0];
14873             name = 0;
14874         }
14875         var s = this.subs[name];
14876         s.buffer[s.buffer.length] = s.tpl.apply(values);
14877         return this;
14878     },
14879
14880     /**
14881      * Applies all the passed values to a child template.
14882      * @param {String/Number} name (optional) The name or index of the child template
14883      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14884      * @param {Boolean} reset (optional) True to reset the template first
14885      * @return {MasterTemplate} this
14886      */
14887     fill : function(name, values, reset){
14888         var a = arguments;
14889         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14890             values = a[0];
14891             name = 0;
14892             reset = a[1];
14893         }
14894         if(reset){
14895             this.reset();
14896         }
14897         for(var i = 0, len = values.length; i < len; i++){
14898             this.add(name, values[i]);
14899         }
14900         return this;
14901     },
14902
14903     /**
14904      * Resets the template for reuse
14905      * @return {MasterTemplate} this
14906      */
14907      reset : function(){
14908         var s = this.subs;
14909         for(var i = 0; i < this.subCount; i++){
14910             s[i].buffer = [];
14911         }
14912         return this;
14913     },
14914
14915     applyTemplate : function(values){
14916         var s = this.subs;
14917         var replaceIndex = -1;
14918         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14919             return s[++replaceIndex].buffer.join("");
14920         });
14921         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14922     },
14923
14924     apply : function(){
14925         return this.applyTemplate.apply(this, arguments);
14926     },
14927
14928     compile : function(){return this;}
14929 });
14930
14931 /**
14932  * Alias for fill().
14933  * @method
14934  */
14935 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14936  /**
14937  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14938  * var tpl = Roo.MasterTemplate.from('element-id');
14939  * @param {String/HTMLElement} el
14940  * @param {Object} config
14941  * @static
14942  */
14943 Roo.MasterTemplate.from = function(el, config){
14944     el = Roo.getDom(el);
14945     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14946 };/*
14947  * Based on:
14948  * Ext JS Library 1.1.1
14949  * Copyright(c) 2006-2007, Ext JS, LLC.
14950  *
14951  * Originally Released Under LGPL - original licence link has changed is not relivant.
14952  *
14953  * Fork - LGPL
14954  * <script type="text/javascript">
14955  */
14956
14957  
14958 /**
14959  * @class Roo.util.CSS
14960  * Utility class for manipulating CSS rules
14961  * @static
14962
14963  */
14964 Roo.util.CSS = function(){
14965         var rules = null;
14966         var doc = document;
14967
14968     var camelRe = /(-[a-z])/gi;
14969     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14970
14971    return {
14972    /**
14973     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14974     * tag and appended to the HEAD of the document.
14975     * @param {String|Object} cssText The text containing the css rules
14976     * @param {String} id An id to add to the stylesheet for later removal
14977     * @return {StyleSheet}
14978     */
14979     createStyleSheet : function(cssText, id){
14980         var ss;
14981         var head = doc.getElementsByTagName("head")[0];
14982         var nrules = doc.createElement("style");
14983         nrules.setAttribute("type", "text/css");
14984         if(id){
14985             nrules.setAttribute("id", id);
14986         }
14987         if (typeof(cssText) != 'string') {
14988             // support object maps..
14989             // not sure if this a good idea.. 
14990             // perhaps it should be merged with the general css handling
14991             // and handle js style props.
14992             var cssTextNew = [];
14993             for(var n in cssText) {
14994                 var citems = [];
14995                 for(var k in cssText[n]) {
14996                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14997                 }
14998                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14999                 
15000             }
15001             cssText = cssTextNew.join("\n");
15002             
15003         }
15004        
15005        
15006        if(Roo.isIE){
15007            head.appendChild(nrules);
15008            ss = nrules.styleSheet;
15009            ss.cssText = cssText;
15010        }else{
15011            try{
15012                 nrules.appendChild(doc.createTextNode(cssText));
15013            }catch(e){
15014                nrules.cssText = cssText; 
15015            }
15016            head.appendChild(nrules);
15017            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15018        }
15019        this.cacheStyleSheet(ss);
15020        return ss;
15021    },
15022
15023    /**
15024     * Removes a style or link tag by id
15025     * @param {String} id The id of the tag
15026     */
15027    removeStyleSheet : function(id){
15028        var existing = doc.getElementById(id);
15029        if(existing){
15030            existing.parentNode.removeChild(existing);
15031        }
15032    },
15033
15034    /**
15035     * Dynamically swaps an existing stylesheet reference for a new one
15036     * @param {String} id The id of an existing link tag to remove
15037     * @param {String} url The href of the new stylesheet to include
15038     */
15039    swapStyleSheet : function(id, url){
15040        this.removeStyleSheet(id);
15041        var ss = doc.createElement("link");
15042        ss.setAttribute("rel", "stylesheet");
15043        ss.setAttribute("type", "text/css");
15044        ss.setAttribute("id", id);
15045        ss.setAttribute("href", url);
15046        doc.getElementsByTagName("head")[0].appendChild(ss);
15047    },
15048    
15049    /**
15050     * Refresh the rule cache if you have dynamically added stylesheets
15051     * @return {Object} An object (hash) of rules indexed by selector
15052     */
15053    refreshCache : function(){
15054        return this.getRules(true);
15055    },
15056
15057    // private
15058    cacheStyleSheet : function(stylesheet){
15059        if(!rules){
15060            rules = {};
15061        }
15062        try{// try catch for cross domain access issue
15063            var ssRules = stylesheet.cssRules || stylesheet.rules;
15064            for(var j = ssRules.length-1; j >= 0; --j){
15065                rules[ssRules[j].selectorText] = ssRules[j];
15066            }
15067        }catch(e){}
15068    },
15069    
15070    /**
15071     * Gets all css rules for the document
15072     * @param {Boolean} refreshCache true to refresh the internal cache
15073     * @return {Object} An object (hash) of rules indexed by selector
15074     */
15075    getRules : function(refreshCache){
15076                 if(rules == null || refreshCache){
15077                         rules = {};
15078                         var ds = doc.styleSheets;
15079                         for(var i =0, len = ds.length; i < len; i++){
15080                             try{
15081                         this.cacheStyleSheet(ds[i]);
15082                     }catch(e){} 
15083                 }
15084                 }
15085                 return rules;
15086         },
15087         
15088         /**
15089     * Gets an an individual CSS rule by selector(s)
15090     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15091     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15092     * @return {CSSRule} The CSS rule or null if one is not found
15093     */
15094    getRule : function(selector, refreshCache){
15095                 var rs = this.getRules(refreshCache);
15096                 if(!(selector instanceof Array)){
15097                     return rs[selector];
15098                 }
15099                 for(var i = 0; i < selector.length; i++){
15100                         if(rs[selector[i]]){
15101                                 return rs[selector[i]];
15102                         }
15103                 }
15104                 return null;
15105         },
15106         
15107         
15108         /**
15109     * Updates a rule property
15110     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15111     * @param {String} property The css property
15112     * @param {String} value The new value for the property
15113     * @return {Boolean} true If a rule was found and updated
15114     */
15115    updateRule : function(selector, property, value){
15116                 if(!(selector instanceof Array)){
15117                         var rule = this.getRule(selector);
15118                         if(rule){
15119                                 rule.style[property.replace(camelRe, camelFn)] = value;
15120                                 return true;
15121                         }
15122                 }else{
15123                         for(var i = 0; i < selector.length; i++){
15124                                 if(this.updateRule(selector[i], property, value)){
15125                                         return true;
15126                                 }
15127                         }
15128                 }
15129                 return false;
15130         }
15131    };   
15132 }();/*
15133  * Based on:
15134  * Ext JS Library 1.1.1
15135  * Copyright(c) 2006-2007, Ext JS, LLC.
15136  *
15137  * Originally Released Under LGPL - original licence link has changed is not relivant.
15138  *
15139  * Fork - LGPL
15140  * <script type="text/javascript">
15141  */
15142
15143  
15144
15145 /**
15146  * @class Roo.util.ClickRepeater
15147  * @extends Roo.util.Observable
15148  * 
15149  * A wrapper class which can be applied to any element. Fires a "click" event while the
15150  * mouse is pressed. The interval between firings may be specified in the config but
15151  * defaults to 10 milliseconds.
15152  * 
15153  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15154  * 
15155  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15156  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15157  * Similar to an autorepeat key delay.
15158  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15159  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15160  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15161  *           "interval" and "delay" are ignored. "immediate" is honored.
15162  * @cfg {Boolean} preventDefault True to prevent the default click event
15163  * @cfg {Boolean} stopDefault True to stop the default click event
15164  * 
15165  * @history
15166  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15167  *     2007-02-02 jvs Renamed to ClickRepeater
15168  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15169  *
15170  *  @constructor
15171  * @param {String/HTMLElement/Element} el The element to listen on
15172  * @param {Object} config
15173  **/
15174 Roo.util.ClickRepeater = function(el, config)
15175 {
15176     this.el = Roo.get(el);
15177     this.el.unselectable();
15178
15179     Roo.apply(this, config);
15180
15181     this.addEvents({
15182     /**
15183      * @event mousedown
15184      * Fires when the mouse button is depressed.
15185      * @param {Roo.util.ClickRepeater} this
15186      */
15187         "mousedown" : true,
15188     /**
15189      * @event click
15190      * Fires on a specified interval during the time the element is pressed.
15191      * @param {Roo.util.ClickRepeater} this
15192      */
15193         "click" : true,
15194     /**
15195      * @event mouseup
15196      * Fires when the mouse key is released.
15197      * @param {Roo.util.ClickRepeater} this
15198      */
15199         "mouseup" : true
15200     });
15201
15202     this.el.on("mousedown", this.handleMouseDown, this);
15203     if(this.preventDefault || this.stopDefault){
15204         this.el.on("click", function(e){
15205             if(this.preventDefault){
15206                 e.preventDefault();
15207             }
15208             if(this.stopDefault){
15209                 e.stopEvent();
15210             }
15211         }, this);
15212     }
15213
15214     // allow inline handler
15215     if(this.handler){
15216         this.on("click", this.handler,  this.scope || this);
15217     }
15218
15219     Roo.util.ClickRepeater.superclass.constructor.call(this);
15220 };
15221
15222 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15223     interval : 20,
15224     delay: 250,
15225     preventDefault : true,
15226     stopDefault : false,
15227     timer : 0,
15228
15229     // private
15230     handleMouseDown : function(){
15231         clearTimeout(this.timer);
15232         this.el.blur();
15233         if(this.pressClass){
15234             this.el.addClass(this.pressClass);
15235         }
15236         this.mousedownTime = new Date();
15237
15238         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15239         this.el.on("mouseout", this.handleMouseOut, this);
15240
15241         this.fireEvent("mousedown", this);
15242         this.fireEvent("click", this);
15243         
15244         this.timer = this.click.defer(this.delay || this.interval, this);
15245     },
15246
15247     // private
15248     click : function(){
15249         this.fireEvent("click", this);
15250         this.timer = this.click.defer(this.getInterval(), this);
15251     },
15252
15253     // private
15254     getInterval: function(){
15255         if(!this.accelerate){
15256             return this.interval;
15257         }
15258         var pressTime = this.mousedownTime.getElapsed();
15259         if(pressTime < 500){
15260             return 400;
15261         }else if(pressTime < 1700){
15262             return 320;
15263         }else if(pressTime < 2600){
15264             return 250;
15265         }else if(pressTime < 3500){
15266             return 180;
15267         }else if(pressTime < 4400){
15268             return 140;
15269         }else if(pressTime < 5300){
15270             return 80;
15271         }else if(pressTime < 6200){
15272             return 50;
15273         }else{
15274             return 10;
15275         }
15276     },
15277
15278     // private
15279     handleMouseOut : function(){
15280         clearTimeout(this.timer);
15281         if(this.pressClass){
15282             this.el.removeClass(this.pressClass);
15283         }
15284         this.el.on("mouseover", this.handleMouseReturn, this);
15285     },
15286
15287     // private
15288     handleMouseReturn : function(){
15289         this.el.un("mouseover", this.handleMouseReturn);
15290         if(this.pressClass){
15291             this.el.addClass(this.pressClass);
15292         }
15293         this.click();
15294     },
15295
15296     // private
15297     handleMouseUp : function(){
15298         clearTimeout(this.timer);
15299         this.el.un("mouseover", this.handleMouseReturn);
15300         this.el.un("mouseout", this.handleMouseOut);
15301         Roo.get(document).un("mouseup", this.handleMouseUp);
15302         this.el.removeClass(this.pressClass);
15303         this.fireEvent("mouseup", this);
15304     }
15305 });/**
15306  * @class Roo.util.Clipboard
15307  * @static
15308  * 
15309  * Clipboard UTILS
15310  * 
15311  **/
15312 Roo.util.Clipboard = {
15313     /**
15314      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15315      * @param {String} text to copy to clipboard
15316      */
15317     write : function(text) {
15318         // navigator clipboard api needs a secure context (https)
15319         if (navigator.clipboard && window.isSecureContext) {
15320             // navigator clipboard api method'
15321             navigator.clipboard.writeText(text);
15322             return ;
15323         } 
15324         // text area method
15325         var ta = document.createElement("textarea");
15326         ta.value = text;
15327         // make the textarea out of viewport
15328         ta.style.position = "fixed";
15329         ta.style.left = "-999999px";
15330         ta.style.top = "-999999px";
15331         document.body.appendChild(ta);
15332         ta.focus();
15333         ta.select();
15334         document.execCommand('copy');
15335         (function() {
15336             ta.remove();
15337         }).defer(100);
15338         
15339     }
15340         
15341 }
15342     /*
15343  * Based on:
15344  * Ext JS Library 1.1.1
15345  * Copyright(c) 2006-2007, Ext JS, LLC.
15346  *
15347  * Originally Released Under LGPL - original licence link has changed is not relivant.
15348  *
15349  * Fork - LGPL
15350  * <script type="text/javascript">
15351  */
15352
15353  
15354 /**
15355  * @class Roo.KeyNav
15356  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15357  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15358  * way to implement custom navigation schemes for any UI component.</p>
15359  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15360  * pageUp, pageDown, del, home, end.  Usage:</p>
15361  <pre><code>
15362 var nav = new Roo.KeyNav("my-element", {
15363     "left" : function(e){
15364         this.moveLeft(e.ctrlKey);
15365     },
15366     "right" : function(e){
15367         this.moveRight(e.ctrlKey);
15368     },
15369     "enter" : function(e){
15370         this.save();
15371     },
15372     scope : this
15373 });
15374 </code></pre>
15375  * @constructor
15376  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15377  * @param {Object} config The config
15378  */
15379 Roo.KeyNav = function(el, config){
15380     this.el = Roo.get(el);
15381     Roo.apply(this, config);
15382     if(!this.disabled){
15383         this.disabled = true;
15384         this.enable();
15385     }
15386 };
15387
15388 Roo.KeyNav.prototype = {
15389     /**
15390      * @cfg {Boolean} disabled
15391      * True to disable this KeyNav instance (defaults to false)
15392      */
15393     disabled : false,
15394     /**
15395      * @cfg {String} defaultEventAction
15396      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15397      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15398      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15399      */
15400     defaultEventAction: "stopEvent",
15401     /**
15402      * @cfg {Boolean} forceKeyDown
15403      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15404      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15405      * handle keydown instead of keypress.
15406      */
15407     forceKeyDown : false,
15408
15409     // private
15410     prepareEvent : function(e){
15411         var k = e.getKey();
15412         var h = this.keyToHandler[k];
15413         //if(h && this[h]){
15414         //    e.stopPropagation();
15415         //}
15416         if(Roo.isSafari && h && k >= 37 && k <= 40){
15417             e.stopEvent();
15418         }
15419     },
15420
15421     // private
15422     relay : function(e){
15423         var k = e.getKey();
15424         var h = this.keyToHandler[k];
15425         if(h && this[h]){
15426             if(this.doRelay(e, this[h], h) !== true){
15427                 e[this.defaultEventAction]();
15428             }
15429         }
15430     },
15431
15432     // private
15433     doRelay : function(e, h, hname){
15434         return h.call(this.scope || this, e);
15435     },
15436
15437     // possible handlers
15438     enter : false,
15439     left : false,
15440     right : false,
15441     up : false,
15442     down : false,
15443     tab : false,
15444     esc : false,
15445     pageUp : false,
15446     pageDown : false,
15447     del : false,
15448     home : false,
15449     end : false,
15450
15451     // quick lookup hash
15452     keyToHandler : {
15453         37 : "left",
15454         39 : "right",
15455         38 : "up",
15456         40 : "down",
15457         33 : "pageUp",
15458         34 : "pageDown",
15459         46 : "del",
15460         36 : "home",
15461         35 : "end",
15462         13 : "enter",
15463         27 : "esc",
15464         9  : "tab"
15465     },
15466
15467         /**
15468          * Enable this KeyNav
15469          */
15470         enable: function(){
15471                 if(this.disabled){
15472             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15473             // the EventObject will normalize Safari automatically
15474             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15475                 this.el.on("keydown", this.relay,  this);
15476             }else{
15477                 this.el.on("keydown", this.prepareEvent,  this);
15478                 this.el.on("keypress", this.relay,  this);
15479             }
15480                     this.disabled = false;
15481                 }
15482         },
15483
15484         /**
15485          * Disable this KeyNav
15486          */
15487         disable: function(){
15488                 if(!this.disabled){
15489                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15490                 this.el.un("keydown", this.relay);
15491             }else{
15492                 this.el.un("keydown", this.prepareEvent);
15493                 this.el.un("keypress", this.relay);
15494             }
15495                     this.disabled = true;
15496                 }
15497         }
15498 };/*
15499  * Based on:
15500  * Ext JS Library 1.1.1
15501  * Copyright(c) 2006-2007, Ext JS, LLC.
15502  *
15503  * Originally Released Under LGPL - original licence link has changed is not relivant.
15504  *
15505  * Fork - LGPL
15506  * <script type="text/javascript">
15507  */
15508
15509  
15510 /**
15511  * @class Roo.KeyMap
15512  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15513  * The constructor accepts the same config object as defined by {@link #addBinding}.
15514  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15515  * combination it will call the function with this signature (if the match is a multi-key
15516  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15517  * A KeyMap can also handle a string representation of keys.<br />
15518  * Usage:
15519  <pre><code>
15520 // map one key by key code
15521 var map = new Roo.KeyMap("my-element", {
15522     key: 13, // or Roo.EventObject.ENTER
15523     fn: myHandler,
15524     scope: myObject
15525 });
15526
15527 // map multiple keys to one action by string
15528 var map = new Roo.KeyMap("my-element", {
15529     key: "a\r\n\t",
15530     fn: myHandler,
15531     scope: myObject
15532 });
15533
15534 // map multiple keys to multiple actions by strings and array of codes
15535 var map = new Roo.KeyMap("my-element", [
15536     {
15537         key: [10,13],
15538         fn: function(){ alert("Return was pressed"); }
15539     }, {
15540         key: "abc",
15541         fn: function(){ alert('a, b or c was pressed'); }
15542     }, {
15543         key: "\t",
15544         ctrl:true,
15545         shift:true,
15546         fn: function(){ alert('Control + shift + tab was pressed.'); }
15547     }
15548 ]);
15549 </code></pre>
15550  * <b>Note: A KeyMap starts enabled</b>
15551  * @constructor
15552  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15553  * @param {Object} config The config (see {@link #addBinding})
15554  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15555  */
15556 Roo.KeyMap = function(el, config, eventName){
15557     this.el  = Roo.get(el);
15558     this.eventName = eventName || "keydown";
15559     this.bindings = [];
15560     if(config){
15561         this.addBinding(config);
15562     }
15563     this.enable();
15564 };
15565
15566 Roo.KeyMap.prototype = {
15567     /**
15568      * True to stop the event from bubbling and prevent the default browser action if the
15569      * key was handled by the KeyMap (defaults to false)
15570      * @type Boolean
15571      */
15572     stopEvent : false,
15573
15574     /**
15575      * Add a new binding to this KeyMap. The following config object properties are supported:
15576      * <pre>
15577 Property    Type             Description
15578 ----------  ---------------  ----------------------------------------------------------------------
15579 key         String/Array     A single keycode or an array of keycodes to handle
15580 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15581 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15582 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15583 fn          Function         The function to call when KeyMap finds the expected key combination
15584 scope       Object           The scope of the callback function
15585 </pre>
15586      *
15587      * Usage:
15588      * <pre><code>
15589 // Create a KeyMap
15590 var map = new Roo.KeyMap(document, {
15591     key: Roo.EventObject.ENTER,
15592     fn: handleKey,
15593     scope: this
15594 });
15595
15596 //Add a new binding to the existing KeyMap later
15597 map.addBinding({
15598     key: 'abc',
15599     shift: true,
15600     fn: handleKey,
15601     scope: this
15602 });
15603 </code></pre>
15604      * @param {Object/Array} config A single KeyMap config or an array of configs
15605      */
15606         addBinding : function(config){
15607         if(config instanceof Array){
15608             for(var i = 0, len = config.length; i < len; i++){
15609                 this.addBinding(config[i]);
15610             }
15611             return;
15612         }
15613         var keyCode = config.key,
15614             shift = config.shift, 
15615             ctrl = config.ctrl, 
15616             alt = config.alt,
15617             fn = config.fn,
15618             scope = config.scope;
15619         if(typeof keyCode == "string"){
15620             var ks = [];
15621             var keyString = keyCode.toUpperCase();
15622             for(var j = 0, len = keyString.length; j < len; j++){
15623                 ks.push(keyString.charCodeAt(j));
15624             }
15625             keyCode = ks;
15626         }
15627         var keyArray = keyCode instanceof Array;
15628         var handler = function(e){
15629             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15630                 var k = e.getKey();
15631                 if(keyArray){
15632                     for(var i = 0, len = keyCode.length; i < len; i++){
15633                         if(keyCode[i] == k){
15634                           if(this.stopEvent){
15635                               e.stopEvent();
15636                           }
15637                           fn.call(scope || window, k, e);
15638                           return;
15639                         }
15640                     }
15641                 }else{
15642                     if(k == keyCode){
15643                         if(this.stopEvent){
15644                            e.stopEvent();
15645                         }
15646                         fn.call(scope || window, k, e);
15647                     }
15648                 }
15649             }
15650         };
15651         this.bindings.push(handler);  
15652         },
15653
15654     /**
15655      * Shorthand for adding a single key listener
15656      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15657      * following options:
15658      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15659      * @param {Function} fn The function to call
15660      * @param {Object} scope (optional) The scope of the function
15661      */
15662     on : function(key, fn, scope){
15663         var keyCode, shift, ctrl, alt;
15664         if(typeof key == "object" && !(key instanceof Array)){
15665             keyCode = key.key;
15666             shift = key.shift;
15667             ctrl = key.ctrl;
15668             alt = key.alt;
15669         }else{
15670             keyCode = key;
15671         }
15672         this.addBinding({
15673             key: keyCode,
15674             shift: shift,
15675             ctrl: ctrl,
15676             alt: alt,
15677             fn: fn,
15678             scope: scope
15679         })
15680     },
15681
15682     // private
15683     handleKeyDown : function(e){
15684             if(this.enabled){ //just in case
15685             var b = this.bindings;
15686             for(var i = 0, len = b.length; i < len; i++){
15687                 b[i].call(this, e);
15688             }
15689             }
15690         },
15691         
15692         /**
15693          * Returns true if this KeyMap is enabled
15694          * @return {Boolean} 
15695          */
15696         isEnabled : function(){
15697             return this.enabled;  
15698         },
15699         
15700         /**
15701          * Enables this KeyMap
15702          */
15703         enable: function(){
15704                 if(!this.enabled){
15705                     this.el.on(this.eventName, this.handleKeyDown, this);
15706                     this.enabled = true;
15707                 }
15708         },
15709
15710         /**
15711          * Disable this KeyMap
15712          */
15713         disable: function(){
15714                 if(this.enabled){
15715                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15716                     this.enabled = false;
15717                 }
15718         }
15719 };/*
15720  * Based on:
15721  * Ext JS Library 1.1.1
15722  * Copyright(c) 2006-2007, Ext JS, LLC.
15723  *
15724  * Originally Released Under LGPL - original licence link has changed is not relivant.
15725  *
15726  * Fork - LGPL
15727  * <script type="text/javascript">
15728  */
15729
15730  
15731 /**
15732  * @class Roo.util.TextMetrics
15733  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15734  * wide, in pixels, a given block of text will be.
15735  * @static
15736  */
15737 Roo.util.TextMetrics = function(){
15738     var shared;
15739     return {
15740         /**
15741          * Measures the size of the specified text
15742          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15743          * that can affect the size of the rendered text
15744          * @param {String} text The text to measure
15745          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15746          * in order to accurately measure the text height
15747          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15748          */
15749         measure : function(el, text, fixedWidth){
15750             if(!shared){
15751                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15752             }
15753             shared.bind(el);
15754             shared.setFixedWidth(fixedWidth || 'auto');
15755             return shared.getSize(text);
15756         },
15757
15758         /**
15759          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15760          * the overhead of multiple calls to initialize the style properties on each measurement.
15761          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15762          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15763          * in order to accurately measure the text height
15764          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15765          */
15766         createInstance : function(el, fixedWidth){
15767             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15768         }
15769     };
15770 }();
15771
15772 /**
15773  * @class Roo.util.TextMetrics.Instance
15774  * Instance of  TextMetrics Calcuation
15775  * @constructor
15776  * Create a new TextMetrics Instance
15777  * @param {Object} bindto
15778  * @param {Boolean} fixedWidth
15779  */
15780
15781 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15782 {
15783     var ml = new Roo.Element(document.createElement('div'));
15784     document.body.appendChild(ml.dom);
15785     ml.position('absolute');
15786     ml.setLeftTop(-1000, -1000);
15787     ml.hide();
15788
15789     if(fixedWidth){
15790         ml.setWidth(fixedWidth);
15791     }
15792      
15793     var instance = {
15794         /**
15795          * Returns the size of the specified text based on the internal element's style and width properties
15796          * @param {String} text The text to measure
15797          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15798          */
15799         getSize : function(text){
15800             ml.update(text);
15801             var s = ml.getSize();
15802             ml.update('');
15803             return s;
15804         },
15805
15806         /**
15807          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15808          * that can affect the size of the rendered text
15809          * @param {String/HTMLElement} el The element, dom node or id
15810          */
15811         bind : function(el){
15812             ml.setStyle(
15813                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15814             );
15815         },
15816
15817         /**
15818          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15819          * to set a fixed width in order to accurately measure the text height.
15820          * @param {Number} width The width to set on the element
15821          */
15822         setFixedWidth : function(width){
15823             ml.setWidth(width);
15824         },
15825
15826         /**
15827          * Returns the measured width of the specified text
15828          * @param {String} text The text to measure
15829          * @return {Number} width The width in pixels
15830          */
15831         getWidth : function(text){
15832             ml.dom.style.width = 'auto';
15833             return this.getSize(text).width;
15834         },
15835
15836         /**
15837          * Returns the measured height of the specified text.  For multiline text, be sure to call
15838          * {@link #setFixedWidth} if necessary.
15839          * @param {String} text The text to measure
15840          * @return {Number} height The height in pixels
15841          */
15842         getHeight : function(text){
15843             return this.getSize(text).height;
15844         }
15845     };
15846
15847     instance.bind(bindTo);
15848
15849     return instance;
15850 };
15851
15852 // backwards compat
15853 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15854  * Based on:
15855  * Ext JS Library 1.1.1
15856  * Copyright(c) 2006-2007, Ext JS, LLC.
15857  *
15858  * Originally Released Under LGPL - original licence link has changed is not relivant.
15859  *
15860  * Fork - LGPL
15861  * <script type="text/javascript">
15862  */
15863
15864 /**
15865  * @class Roo.state.Provider
15866  * Abstract base class for state provider implementations. This class provides methods
15867  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15868  * Provider interface.
15869  */
15870 Roo.state.Provider = function(){
15871     /**
15872      * @event statechange
15873      * Fires when a state change occurs.
15874      * @param {Provider} this This state provider
15875      * @param {String} key The state key which was changed
15876      * @param {String} value The encoded value for the state
15877      */
15878     this.addEvents({
15879         "statechange": true
15880     });
15881     this.state = {};
15882     Roo.state.Provider.superclass.constructor.call(this);
15883 };
15884 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15885     /**
15886      * Returns the current value for a key
15887      * @param {String} name The key name
15888      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15889      * @return {Mixed} The state data
15890      */
15891     get : function(name, defaultValue){
15892         return typeof this.state[name] == "undefined" ?
15893             defaultValue : this.state[name];
15894     },
15895     
15896     /**
15897      * Clears a value from the state
15898      * @param {String} name The key name
15899      */
15900     clear : function(name){
15901         delete this.state[name];
15902         this.fireEvent("statechange", this, name, null);
15903     },
15904     
15905     /**
15906      * Sets the value for a key
15907      * @param {String} name The key name
15908      * @param {Mixed} value The value to set
15909      */
15910     set : function(name, value){
15911         this.state[name] = value;
15912         this.fireEvent("statechange", this, name, value);
15913     },
15914     
15915     /**
15916      * Decodes a string previously encoded with {@link #encodeValue}.
15917      * @param {String} value The value to decode
15918      * @return {Mixed} The decoded value
15919      */
15920     decodeValue : function(cookie){
15921         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15922         var matches = re.exec(unescape(cookie));
15923         if(!matches || !matches[1]) {
15924             return; // non state cookie
15925         }
15926         var type = matches[1];
15927         var v = matches[2];
15928         switch(type){
15929             case "n":
15930                 return parseFloat(v);
15931             case "d":
15932                 return new Date(Date.parse(v));
15933             case "b":
15934                 return (v == "1");
15935             case "a":
15936                 var all = [];
15937                 var values = v.split("^");
15938                 for(var i = 0, len = values.length; i < len; i++){
15939                     all.push(this.decodeValue(values[i]));
15940                 }
15941                 return all;
15942            case "o":
15943                 var all = {};
15944                 var values = v.split("^");
15945                 for(var i = 0, len = values.length; i < len; i++){
15946                     var kv = values[i].split("=");
15947                     all[kv[0]] = this.decodeValue(kv[1]);
15948                 }
15949                 return all;
15950            default:
15951                 return v;
15952         }
15953     },
15954     
15955     /**
15956      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15957      * @param {Mixed} value The value to encode
15958      * @return {String} The encoded value
15959      */
15960     encodeValue : function(v){
15961         var enc;
15962         if(typeof v == "number"){
15963             enc = "n:" + v;
15964         }else if(typeof v == "boolean"){
15965             enc = "b:" + (v ? "1" : "0");
15966         }else if(v instanceof Date){
15967             enc = "d:" + v.toGMTString();
15968         }else if(v instanceof Array){
15969             var flat = "";
15970             for(var i = 0, len = v.length; i < len; i++){
15971                 flat += this.encodeValue(v[i]);
15972                 if(i != len-1) {
15973                     flat += "^";
15974                 }
15975             }
15976             enc = "a:" + flat;
15977         }else if(typeof v == "object"){
15978             var flat = "";
15979             for(var key in v){
15980                 if(typeof v[key] != "function"){
15981                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15982                 }
15983             }
15984             enc = "o:" + flat.substring(0, flat.length-1);
15985         }else{
15986             enc = "s:" + v;
15987         }
15988         return escape(enc);        
15989     }
15990 });
15991
15992 /*
15993  * Based on:
15994  * Ext JS Library 1.1.1
15995  * Copyright(c) 2006-2007, Ext JS, LLC.
15996  *
15997  * Originally Released Under LGPL - original licence link has changed is not relivant.
15998  *
15999  * Fork - LGPL
16000  * <script type="text/javascript">
16001  */
16002 /**
16003  * @class Roo.state.Manager
16004  * This is the global state manager. By default all components that are "state aware" check this class
16005  * for state information if you don't pass them a custom state provider. In order for this class
16006  * to be useful, it must be initialized with a provider when your application initializes.
16007  <pre><code>
16008 // in your initialization function
16009 init : function(){
16010    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16011    ...
16012    // supposed you have a {@link Roo.BorderLayout}
16013    var layout = new Roo.BorderLayout(...);
16014    layout.restoreState();
16015    // or a {Roo.BasicDialog}
16016    var dialog = new Roo.BasicDialog(...);
16017    dialog.restoreState();
16018  </code></pre>
16019  * @static
16020  */
16021 Roo.state.Manager = function(){
16022     var provider = new Roo.state.Provider();
16023     
16024     return {
16025         /**
16026          * Configures the default state provider for your application
16027          * @param {Provider} stateProvider The state provider to set
16028          */
16029         setProvider : function(stateProvider){
16030             provider = stateProvider;
16031         },
16032         
16033         /**
16034          * Returns the current value for a key
16035          * @param {String} name The key name
16036          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16037          * @return {Mixed} The state data
16038          */
16039         get : function(key, defaultValue){
16040             return provider.get(key, defaultValue);
16041         },
16042         
16043         /**
16044          * Sets the value for a key
16045          * @param {String} name The key name
16046          * @param {Mixed} value The state data
16047          */
16048          set : function(key, value){
16049             provider.set(key, value);
16050         },
16051         
16052         /**
16053          * Clears a value from the state
16054          * @param {String} name The key name
16055          */
16056         clear : function(key){
16057             provider.clear(key);
16058         },
16059         
16060         /**
16061          * Gets the currently configured state provider
16062          * @return {Provider} The state provider
16063          */
16064         getProvider : function(){
16065             return provider;
16066         }
16067     };
16068 }();
16069 /*
16070  * Based on:
16071  * Ext JS Library 1.1.1
16072  * Copyright(c) 2006-2007, Ext JS, LLC.
16073  *
16074  * Originally Released Under LGPL - original licence link has changed is not relivant.
16075  *
16076  * Fork - LGPL
16077  * <script type="text/javascript">
16078  */
16079 /**
16080  * @class Roo.state.CookieProvider
16081  * @extends Roo.state.Provider
16082  * The default Provider implementation which saves state via cookies.
16083  * <br />Usage:
16084  <pre><code>
16085    var cp = new Roo.state.CookieProvider({
16086        path: "/cgi-bin/",
16087        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16088        domain: "roojs.com"
16089    })
16090    Roo.state.Manager.setProvider(cp);
16091  </code></pre>
16092  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16093  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16094  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16095  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16096  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16097  * domain the page is running on including the 'www' like 'www.roojs.com')
16098  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16099  * @constructor
16100  * Create a new CookieProvider
16101  * @param {Object} config The configuration object
16102  */
16103 Roo.state.CookieProvider = function(config){
16104     Roo.state.CookieProvider.superclass.constructor.call(this);
16105     this.path = "/";
16106     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16107     this.domain = null;
16108     this.secure = false;
16109     Roo.apply(this, config);
16110     this.state = this.readCookies();
16111 };
16112
16113 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16114     // private
16115     set : function(name, value){
16116         if(typeof value == "undefined" || value === null){
16117             this.clear(name);
16118             return;
16119         }
16120         this.setCookie(name, value);
16121         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16122     },
16123
16124     // private
16125     clear : function(name){
16126         this.clearCookie(name);
16127         Roo.state.CookieProvider.superclass.clear.call(this, name);
16128     },
16129
16130     // private
16131     readCookies : function(){
16132         var cookies = {};
16133         var c = document.cookie + ";";
16134         var re = /\s?(.*?)=(.*?);/g;
16135         var matches;
16136         while((matches = re.exec(c)) != null){
16137             var name = matches[1];
16138             var value = matches[2];
16139             if(name && name.substring(0,3) == "ys-"){
16140                 cookies[name.substr(3)] = this.decodeValue(value);
16141             }
16142         }
16143         return cookies;
16144     },
16145
16146     // private
16147     setCookie : function(name, value){
16148         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16149            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16150            ((this.path == null) ? "" : ("; path=" + this.path)) +
16151            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16152            ((this.secure == true) ? "; secure" : "");
16153     },
16154
16155     // private
16156     clearCookie : function(name){
16157         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16158            ((this.path == null) ? "" : ("; path=" + this.path)) +
16159            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16160            ((this.secure == true) ? "; secure" : "");
16161     }
16162 });/*
16163  * Based on:
16164  * Ext JS Library 1.1.1
16165  * Copyright(c) 2006-2007, Ext JS, LLC.
16166  *
16167  * Originally Released Under LGPL - original licence link has changed is not relivant.
16168  *
16169  * Fork - LGPL
16170  * <script type="text/javascript">
16171  */
16172  
16173
16174 /**
16175  * @class Roo.ComponentMgr
16176  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16177  * @static
16178  */
16179 Roo.ComponentMgr = function(){
16180     var all = new Roo.util.MixedCollection();
16181
16182     return {
16183         /**
16184          * Registers a component.
16185          * @param {Roo.Component} c The component
16186          */
16187         register : function(c){
16188             all.add(c);
16189         },
16190
16191         /**
16192          * Unregisters a component.
16193          * @param {Roo.Component} c The component
16194          */
16195         unregister : function(c){
16196             all.remove(c);
16197         },
16198
16199         /**
16200          * Returns a component by id
16201          * @param {String} id The component id
16202          */
16203         get : function(id){
16204             return all.get(id);
16205         },
16206
16207         /**
16208          * Registers a function that will be called when a specified component is added to ComponentMgr
16209          * @param {String} id The component id
16210          * @param {Funtction} fn The callback function
16211          * @param {Object} scope The scope of the callback
16212          */
16213         onAvailable : function(id, fn, scope){
16214             all.on("add", function(index, o){
16215                 if(o.id == id){
16216                     fn.call(scope || o, o);
16217                     all.un("add", fn, scope);
16218                 }
16219             });
16220         }
16221     };
16222 }();/*
16223  * Based on:
16224  * Ext JS Library 1.1.1
16225  * Copyright(c) 2006-2007, Ext JS, LLC.
16226  *
16227  * Originally Released Under LGPL - original licence link has changed is not relivant.
16228  *
16229  * Fork - LGPL
16230  * <script type="text/javascript">
16231  */
16232  
16233 /**
16234  * @class Roo.Component
16235  * @extends Roo.util.Observable
16236  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16237  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16238  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16239  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16240  * All visual components (widgets) that require rendering into a layout should subclass Component.
16241  * @constructor
16242  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16243  * 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
16244  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16245  */
16246 Roo.Component = function(config){
16247     config = config || {};
16248     if(config.tagName || config.dom || typeof config == "string"){ // element object
16249         config = {el: config, id: config.id || config};
16250     }
16251     this.initialConfig = config;
16252
16253     Roo.apply(this, config);
16254     this.addEvents({
16255         /**
16256          * @event disable
16257          * Fires after the component is disabled.
16258              * @param {Roo.Component} this
16259              */
16260         disable : true,
16261         /**
16262          * @event enable
16263          * Fires after the component is enabled.
16264              * @param {Roo.Component} this
16265              */
16266         enable : true,
16267         /**
16268          * @event beforeshow
16269          * Fires before the component is shown.  Return false to stop the show.
16270              * @param {Roo.Component} this
16271              */
16272         beforeshow : true,
16273         /**
16274          * @event show
16275          * Fires after the component is shown.
16276              * @param {Roo.Component} this
16277              */
16278         show : true,
16279         /**
16280          * @event beforehide
16281          * Fires before the component is hidden. Return false to stop the hide.
16282              * @param {Roo.Component} this
16283              */
16284         beforehide : true,
16285         /**
16286          * @event hide
16287          * Fires after the component is hidden.
16288              * @param {Roo.Component} this
16289              */
16290         hide : true,
16291         /**
16292          * @event beforerender
16293          * Fires before the component is rendered. Return false to stop the render.
16294              * @param {Roo.Component} this
16295              */
16296         beforerender : true,
16297         /**
16298          * @event render
16299          * Fires after the component is rendered.
16300              * @param {Roo.Component} this
16301              */
16302         render : true,
16303         /**
16304          * @event beforedestroy
16305          * Fires before the component is destroyed. Return false to stop the destroy.
16306              * @param {Roo.Component} this
16307              */
16308         beforedestroy : true,
16309         /**
16310          * @event destroy
16311          * Fires after the component is destroyed.
16312              * @param {Roo.Component} this
16313              */
16314         destroy : true
16315     });
16316     if(!this.id){
16317         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16318     }
16319     Roo.ComponentMgr.register(this);
16320     Roo.Component.superclass.constructor.call(this);
16321     this.initComponent();
16322     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16323         this.render(this.renderTo);
16324         delete this.renderTo;
16325     }
16326 };
16327
16328 /** @private */
16329 Roo.Component.AUTO_ID = 1000;
16330
16331 Roo.extend(Roo.Component, Roo.util.Observable, {
16332     /**
16333      * @scope Roo.Component.prototype
16334      * @type {Boolean}
16335      * true if this component is hidden. Read-only.
16336      */
16337     hidden : false,
16338     /**
16339      * @type {Boolean}
16340      * true if this component is disabled. Read-only.
16341      */
16342     disabled : false,
16343     /**
16344      * @type {Boolean}
16345      * true if this component has been rendered. Read-only.
16346      */
16347     rendered : false,
16348     
16349     /** @cfg {String} disableClass
16350      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16351      */
16352     disabledClass : "x-item-disabled",
16353         /** @cfg {Boolean} allowDomMove
16354          * Whether the component can move the Dom node when rendering (defaults to true).
16355          */
16356     allowDomMove : true,
16357     /** @cfg {String} hideMode (display|visibility)
16358      * How this component should hidden. Supported values are
16359      * "visibility" (css visibility), "offsets" (negative offset position) and
16360      * "display" (css display) - defaults to "display".
16361      */
16362     hideMode: 'display',
16363
16364     /** @private */
16365     ctype : "Roo.Component",
16366
16367     /**
16368      * @cfg {String} actionMode 
16369      * which property holds the element that used for  hide() / show() / disable() / enable()
16370      * default is 'el' for forms you probably want to set this to fieldEl 
16371      */
16372     actionMode : "el",
16373
16374     /** @private */
16375     getActionEl : function(){
16376         return this[this.actionMode];
16377     },
16378
16379     initComponent : Roo.emptyFn,
16380     /**
16381      * If this is a lazy rendering component, render it to its container element.
16382      * @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.
16383      */
16384     render : function(container, position){
16385         
16386         if(this.rendered){
16387             return this;
16388         }
16389         
16390         if(this.fireEvent("beforerender", this) === false){
16391             return false;
16392         }
16393         
16394         if(!container && this.el){
16395             this.el = Roo.get(this.el);
16396             container = this.el.dom.parentNode;
16397             this.allowDomMove = false;
16398         }
16399         this.container = Roo.get(container);
16400         this.rendered = true;
16401         if(position !== undefined){
16402             if(typeof position == 'number'){
16403                 position = this.container.dom.childNodes[position];
16404             }else{
16405                 position = Roo.getDom(position);
16406             }
16407         }
16408         this.onRender(this.container, position || null);
16409         if(this.cls){
16410             this.el.addClass(this.cls);
16411             delete this.cls;
16412         }
16413         if(this.style){
16414             this.el.applyStyles(this.style);
16415             delete this.style;
16416         }
16417         this.fireEvent("render", this);
16418         this.afterRender(this.container);
16419         if(this.hidden){
16420             this.hide();
16421         }
16422         if(this.disabled){
16423             this.disable();
16424         }
16425
16426         return this;
16427         
16428     },
16429
16430     /** @private */
16431     // default function is not really useful
16432     onRender : function(ct, position){
16433         if(this.el){
16434             this.el = Roo.get(this.el);
16435             if(this.allowDomMove !== false){
16436                 ct.dom.insertBefore(this.el.dom, position);
16437             }
16438         }
16439     },
16440
16441     /** @private */
16442     getAutoCreate : function(){
16443         var cfg = typeof this.autoCreate == "object" ?
16444                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16445         if(this.id && !cfg.id){
16446             cfg.id = this.id;
16447         }
16448         return cfg;
16449     },
16450
16451     /** @private */
16452     afterRender : Roo.emptyFn,
16453
16454     /**
16455      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16456      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16457      */
16458     destroy : function(){
16459         if(this.fireEvent("beforedestroy", this) !== false){
16460             this.purgeListeners();
16461             this.beforeDestroy();
16462             if(this.rendered){
16463                 this.el.removeAllListeners();
16464                 this.el.remove();
16465                 if(this.actionMode == "container"){
16466                     this.container.remove();
16467                 }
16468             }
16469             this.onDestroy();
16470             Roo.ComponentMgr.unregister(this);
16471             this.fireEvent("destroy", this);
16472         }
16473     },
16474
16475         /** @private */
16476     beforeDestroy : function(){
16477
16478     },
16479
16480         /** @private */
16481         onDestroy : function(){
16482
16483     },
16484
16485     /**
16486      * Returns the underlying {@link Roo.Element}.
16487      * @return {Roo.Element} The element
16488      */
16489     getEl : function(){
16490         return this.el;
16491     },
16492
16493     /**
16494      * Returns the id of this component.
16495      * @return {String}
16496      */
16497     getId : function(){
16498         return this.id;
16499     },
16500
16501     /**
16502      * Try to focus this component.
16503      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16504      * @return {Roo.Component} this
16505      */
16506     focus : function(selectText){
16507         if(this.rendered){
16508             this.el.focus();
16509             if(selectText === true){
16510                 this.el.dom.select();
16511             }
16512         }
16513         return this;
16514     },
16515
16516     /** @private */
16517     blur : function(){
16518         if(this.rendered){
16519             this.el.blur();
16520         }
16521         return this;
16522     },
16523
16524     /**
16525      * Disable this component.
16526      * @return {Roo.Component} this
16527      */
16528     disable : function(){
16529         if(this.rendered){
16530             this.onDisable();
16531         }
16532         this.disabled = true;
16533         this.fireEvent("disable", this);
16534         return this;
16535     },
16536
16537         // private
16538     onDisable : function(){
16539         this.getActionEl().addClass(this.disabledClass);
16540         this.el.dom.disabled = true;
16541     },
16542
16543     /**
16544      * Enable this component.
16545      * @return {Roo.Component} this
16546      */
16547     enable : function(){
16548         if(this.rendered){
16549             this.onEnable();
16550         }
16551         this.disabled = false;
16552         this.fireEvent("enable", this);
16553         return this;
16554     },
16555
16556         // private
16557     onEnable : function(){
16558         this.getActionEl().removeClass(this.disabledClass);
16559         this.el.dom.disabled = false;
16560     },
16561
16562     /**
16563      * Convenience function for setting disabled/enabled by boolean.
16564      * @param {Boolean} disabled
16565      */
16566     setDisabled : function(disabled){
16567         this[disabled ? "disable" : "enable"]();
16568     },
16569
16570     /**
16571      * Show this component.
16572      * @return {Roo.Component} this
16573      */
16574     show: function(){
16575         if(this.fireEvent("beforeshow", this) !== false){
16576             this.hidden = false;
16577             if(this.rendered){
16578                 this.onShow();
16579             }
16580             this.fireEvent("show", this);
16581         }
16582         return this;
16583     },
16584
16585     // private
16586     onShow : function(){
16587         var ae = this.getActionEl();
16588         if(this.hideMode == 'visibility'){
16589             ae.dom.style.visibility = "visible";
16590         }else if(this.hideMode == 'offsets'){
16591             ae.removeClass('x-hidden');
16592         }else{
16593             ae.dom.style.display = "";
16594         }
16595     },
16596
16597     /**
16598      * Hide this component.
16599      * @return {Roo.Component} this
16600      */
16601     hide: function(){
16602         if(this.fireEvent("beforehide", this) !== false){
16603             this.hidden = true;
16604             if(this.rendered){
16605                 this.onHide();
16606             }
16607             this.fireEvent("hide", this);
16608         }
16609         return this;
16610     },
16611
16612     // private
16613     onHide : function(){
16614         var ae = this.getActionEl();
16615         if(this.hideMode == 'visibility'){
16616             ae.dom.style.visibility = "hidden";
16617         }else if(this.hideMode == 'offsets'){
16618             ae.addClass('x-hidden');
16619         }else{
16620             ae.dom.style.display = "none";
16621         }
16622     },
16623
16624     /**
16625      * Convenience function to hide or show this component by boolean.
16626      * @param {Boolean} visible True to show, false to hide
16627      * @return {Roo.Component} this
16628      */
16629     setVisible: function(visible){
16630         if(visible) {
16631             this.show();
16632         }else{
16633             this.hide();
16634         }
16635         return this;
16636     },
16637
16638     /**
16639      * Returns true if this component is visible.
16640      */
16641     isVisible : function(){
16642         return this.getActionEl().isVisible();
16643     },
16644
16645     cloneConfig : function(overrides){
16646         overrides = overrides || {};
16647         var id = overrides.id || Roo.id();
16648         var cfg = Roo.applyIf(overrides, this.initialConfig);
16649         cfg.id = id; // prevent dup id
16650         return new this.constructor(cfg);
16651     }
16652 });/*
16653  * Based on:
16654  * Ext JS Library 1.1.1
16655  * Copyright(c) 2006-2007, Ext JS, LLC.
16656  *
16657  * Originally Released Under LGPL - original licence link has changed is not relivant.
16658  *
16659  * Fork - LGPL
16660  * <script type="text/javascript">
16661  */
16662
16663 /**
16664  * @class Roo.BoxComponent
16665  * @extends Roo.Component
16666  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16667  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16668  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16669  * layout containers.
16670  * @constructor
16671  * @param {Roo.Element/String/Object} config The configuration options.
16672  */
16673 Roo.BoxComponent = function(config){
16674     Roo.Component.call(this, config);
16675     this.addEvents({
16676         /**
16677          * @event resize
16678          * Fires after the component is resized.
16679              * @param {Roo.Component} this
16680              * @param {Number} adjWidth The box-adjusted width that was set
16681              * @param {Number} adjHeight The box-adjusted height that was set
16682              * @param {Number} rawWidth The width that was originally specified
16683              * @param {Number} rawHeight The height that was originally specified
16684              */
16685         resize : true,
16686         /**
16687          * @event move
16688          * Fires after the component is moved.
16689              * @param {Roo.Component} this
16690              * @param {Number} x The new x position
16691              * @param {Number} y The new y position
16692              */
16693         move : true
16694     });
16695 };
16696
16697 Roo.extend(Roo.BoxComponent, Roo.Component, {
16698     // private, set in afterRender to signify that the component has been rendered
16699     boxReady : false,
16700     // private, used to defer height settings to subclasses
16701     deferHeight: false,
16702     /** @cfg {Number} width
16703      * width (optional) size of component
16704      */
16705      /** @cfg {Number} height
16706      * height (optional) size of component
16707      */
16708      
16709     /**
16710      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16711      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16712      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16713      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16714      * @return {Roo.BoxComponent} this
16715      */
16716     setSize : function(w, h){
16717         // support for standard size objects
16718         if(typeof w == 'object'){
16719             h = w.height;
16720             w = w.width;
16721         }
16722         // not rendered
16723         if(!this.boxReady){
16724             this.width = w;
16725             this.height = h;
16726             return this;
16727         }
16728
16729         // prevent recalcs when not needed
16730         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16731             return this;
16732         }
16733         this.lastSize = {width: w, height: h};
16734
16735         var adj = this.adjustSize(w, h);
16736         var aw = adj.width, ah = adj.height;
16737         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16738             var rz = this.getResizeEl();
16739             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16740                 rz.setSize(aw, ah);
16741             }else if(!this.deferHeight && ah !== undefined){
16742                 rz.setHeight(ah);
16743             }else if(aw !== undefined){
16744                 rz.setWidth(aw);
16745             }
16746             this.onResize(aw, ah, w, h);
16747             this.fireEvent('resize', this, aw, ah, w, h);
16748         }
16749         return this;
16750     },
16751
16752     /**
16753      * Gets the current size of the component's underlying element.
16754      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16755      */
16756     getSize : function(){
16757         return this.el.getSize();
16758     },
16759
16760     /**
16761      * Gets the current XY position of the component's underlying element.
16762      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16763      * @return {Array} The XY position of the element (e.g., [100, 200])
16764      */
16765     getPosition : function(local){
16766         if(local === true){
16767             return [this.el.getLeft(true), this.el.getTop(true)];
16768         }
16769         return this.xy || this.el.getXY();
16770     },
16771
16772     /**
16773      * Gets the current box measurements of the component's underlying element.
16774      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16775      * @returns {Object} box An object in the format {x, y, width, height}
16776      */
16777     getBox : function(local){
16778         var s = this.el.getSize();
16779         if(local){
16780             s.x = this.el.getLeft(true);
16781             s.y = this.el.getTop(true);
16782         }else{
16783             var xy = this.xy || this.el.getXY();
16784             s.x = xy[0];
16785             s.y = xy[1];
16786         }
16787         return s;
16788     },
16789
16790     /**
16791      * Sets the current box measurements of the component's underlying element.
16792      * @param {Object} box An object in the format {x, y, width, height}
16793      * @returns {Roo.BoxComponent} this
16794      */
16795     updateBox : function(box){
16796         this.setSize(box.width, box.height);
16797         this.setPagePosition(box.x, box.y);
16798         return this;
16799     },
16800
16801     // protected
16802     getResizeEl : function(){
16803         return this.resizeEl || this.el;
16804     },
16805
16806     // protected
16807     getPositionEl : function(){
16808         return this.positionEl || this.el;
16809     },
16810
16811     /**
16812      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16813      * This method fires the move event.
16814      * @param {Number} left The new left
16815      * @param {Number} top The new top
16816      * @returns {Roo.BoxComponent} this
16817      */
16818     setPosition : function(x, y){
16819         this.x = x;
16820         this.y = y;
16821         if(!this.boxReady){
16822             return this;
16823         }
16824         var adj = this.adjustPosition(x, y);
16825         var ax = adj.x, ay = adj.y;
16826
16827         var el = this.getPositionEl();
16828         if(ax !== undefined || ay !== undefined){
16829             if(ax !== undefined && ay !== undefined){
16830                 el.setLeftTop(ax, ay);
16831             }else if(ax !== undefined){
16832                 el.setLeft(ax);
16833             }else if(ay !== undefined){
16834                 el.setTop(ay);
16835             }
16836             this.onPosition(ax, ay);
16837             this.fireEvent('move', this, ax, ay);
16838         }
16839         return this;
16840     },
16841
16842     /**
16843      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16844      * This method fires the move event.
16845      * @param {Number} x The new x position
16846      * @param {Number} y The new y position
16847      * @returns {Roo.BoxComponent} this
16848      */
16849     setPagePosition : function(x, y){
16850         this.pageX = x;
16851         this.pageY = y;
16852         if(!this.boxReady){
16853             return;
16854         }
16855         if(x === undefined || y === undefined){ // cannot translate undefined points
16856             return;
16857         }
16858         var p = this.el.translatePoints(x, y);
16859         this.setPosition(p.left, p.top);
16860         return this;
16861     },
16862
16863     // private
16864     onRender : function(ct, position){
16865         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16866         if(this.resizeEl){
16867             this.resizeEl = Roo.get(this.resizeEl);
16868         }
16869         if(this.positionEl){
16870             this.positionEl = Roo.get(this.positionEl);
16871         }
16872     },
16873
16874     // private
16875     afterRender : function(){
16876         Roo.BoxComponent.superclass.afterRender.call(this);
16877         this.boxReady = true;
16878         this.setSize(this.width, this.height);
16879         if(this.x || this.y){
16880             this.setPosition(this.x, this.y);
16881         }
16882         if(this.pageX || this.pageY){
16883             this.setPagePosition(this.pageX, this.pageY);
16884         }
16885     },
16886
16887     /**
16888      * Force the component's size to recalculate based on the underlying element's current height and width.
16889      * @returns {Roo.BoxComponent} this
16890      */
16891     syncSize : function(){
16892         delete this.lastSize;
16893         this.setSize(this.el.getWidth(), this.el.getHeight());
16894         return this;
16895     },
16896
16897     /**
16898      * Called after the component is resized, this method is empty by default but can be implemented by any
16899      * subclass that needs to perform custom logic after a resize occurs.
16900      * @param {Number} adjWidth The box-adjusted width that was set
16901      * @param {Number} adjHeight The box-adjusted height that was set
16902      * @param {Number} rawWidth The width that was originally specified
16903      * @param {Number} rawHeight The height that was originally specified
16904      */
16905     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16906
16907     },
16908
16909     /**
16910      * Called after the component is moved, this method is empty by default but can be implemented by any
16911      * subclass that needs to perform custom logic after a move occurs.
16912      * @param {Number} x The new x position
16913      * @param {Number} y The new y position
16914      */
16915     onPosition : function(x, y){
16916
16917     },
16918
16919     // private
16920     adjustSize : function(w, h){
16921         if(this.autoWidth){
16922             w = 'auto';
16923         }
16924         if(this.autoHeight){
16925             h = 'auto';
16926         }
16927         return {width : w, height: h};
16928     },
16929
16930     // private
16931     adjustPosition : function(x, y){
16932         return {x : x, y: y};
16933     }
16934 });/*
16935  * Based on:
16936  * Ext JS Library 1.1.1
16937  * Copyright(c) 2006-2007, Ext JS, LLC.
16938  *
16939  * Originally Released Under LGPL - original licence link has changed is not relivant.
16940  *
16941  * Fork - LGPL
16942  * <script type="text/javascript">
16943  */
16944  (function(){ 
16945 /**
16946  * @class Roo.Layer
16947  * @extends Roo.Element
16948  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16949  * automatic maintaining of shadow/shim positions.
16950  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16951  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16952  * you can pass a string with a CSS class name. False turns off the shadow.
16953  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16954  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16955  * @cfg {String} cls CSS class to add to the element
16956  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16957  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16958  * @constructor
16959  * @param {Object} config An object with config options.
16960  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16961  */
16962
16963 Roo.Layer = function(config, existingEl){
16964     config = config || {};
16965     var dh = Roo.DomHelper;
16966     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16967     if(existingEl){
16968         this.dom = Roo.getDom(existingEl);
16969     }
16970     if(!this.dom){
16971         var o = config.dh || {tag: "div", cls: "x-layer"};
16972         this.dom = dh.append(pel, o);
16973     }
16974     if(config.cls){
16975         this.addClass(config.cls);
16976     }
16977     this.constrain = config.constrain !== false;
16978     this.visibilityMode = Roo.Element.VISIBILITY;
16979     if(config.id){
16980         this.id = this.dom.id = config.id;
16981     }else{
16982         this.id = Roo.id(this.dom);
16983     }
16984     this.zindex = config.zindex || this.getZIndex();
16985     this.position("absolute", this.zindex);
16986     if(config.shadow){
16987         this.shadowOffset = config.shadowOffset || 4;
16988         this.shadow = new Roo.Shadow({
16989             offset : this.shadowOffset,
16990             mode : config.shadow
16991         });
16992     }else{
16993         this.shadowOffset = 0;
16994     }
16995     this.useShim = config.shim !== false && Roo.useShims;
16996     this.useDisplay = config.useDisplay;
16997     this.hide();
16998 };
16999
17000 var supr = Roo.Element.prototype;
17001
17002 // shims are shared among layer to keep from having 100 iframes
17003 var shims = [];
17004
17005 Roo.extend(Roo.Layer, Roo.Element, {
17006
17007     getZIndex : function(){
17008         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17009     },
17010
17011     getShim : function(){
17012         if(!this.useShim){
17013             return null;
17014         }
17015         if(this.shim){
17016             return this.shim;
17017         }
17018         var shim = shims.shift();
17019         if(!shim){
17020             shim = this.createShim();
17021             shim.enableDisplayMode('block');
17022             shim.dom.style.display = 'none';
17023             shim.dom.style.visibility = 'visible';
17024         }
17025         var pn = this.dom.parentNode;
17026         if(shim.dom.parentNode != pn){
17027             pn.insertBefore(shim.dom, this.dom);
17028         }
17029         shim.setStyle('z-index', this.getZIndex()-2);
17030         this.shim = shim;
17031         return shim;
17032     },
17033
17034     hideShim : function(){
17035         if(this.shim){
17036             this.shim.setDisplayed(false);
17037             shims.push(this.shim);
17038             delete this.shim;
17039         }
17040     },
17041
17042     disableShadow : function(){
17043         if(this.shadow){
17044             this.shadowDisabled = true;
17045             this.shadow.hide();
17046             this.lastShadowOffset = this.shadowOffset;
17047             this.shadowOffset = 0;
17048         }
17049     },
17050
17051     enableShadow : function(show){
17052         if(this.shadow){
17053             this.shadowDisabled = false;
17054             this.shadowOffset = this.lastShadowOffset;
17055             delete this.lastShadowOffset;
17056             if(show){
17057                 this.sync(true);
17058             }
17059         }
17060     },
17061
17062     // private
17063     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17064     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17065     sync : function(doShow){
17066         var sw = this.shadow;
17067         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17068             var sh = this.getShim();
17069
17070             var w = this.getWidth(),
17071                 h = this.getHeight();
17072
17073             var l = this.getLeft(true),
17074                 t = this.getTop(true);
17075
17076             if(sw && !this.shadowDisabled){
17077                 if(doShow && !sw.isVisible()){
17078                     sw.show(this);
17079                 }else{
17080                     sw.realign(l, t, w, h);
17081                 }
17082                 if(sh){
17083                     if(doShow){
17084                        sh.show();
17085                     }
17086                     // fit the shim behind the shadow, so it is shimmed too
17087                     var a = sw.adjusts, s = sh.dom.style;
17088                     s.left = (Math.min(l, l+a.l))+"px";
17089                     s.top = (Math.min(t, t+a.t))+"px";
17090                     s.width = (w+a.w)+"px";
17091                     s.height = (h+a.h)+"px";
17092                 }
17093             }else if(sh){
17094                 if(doShow){
17095                    sh.show();
17096                 }
17097                 sh.setSize(w, h);
17098                 sh.setLeftTop(l, t);
17099             }
17100             
17101         }
17102     },
17103
17104     // private
17105     destroy : function(){
17106         this.hideShim();
17107         if(this.shadow){
17108             this.shadow.hide();
17109         }
17110         this.removeAllListeners();
17111         var pn = this.dom.parentNode;
17112         if(pn){
17113             pn.removeChild(this.dom);
17114         }
17115         Roo.Element.uncache(this.id);
17116     },
17117
17118     remove : function(){
17119         this.destroy();
17120     },
17121
17122     // private
17123     beginUpdate : function(){
17124         this.updating = true;
17125     },
17126
17127     // private
17128     endUpdate : function(){
17129         this.updating = false;
17130         this.sync(true);
17131     },
17132
17133     // private
17134     hideUnders : function(negOffset){
17135         if(this.shadow){
17136             this.shadow.hide();
17137         }
17138         this.hideShim();
17139     },
17140
17141     // private
17142     constrainXY : function(){
17143         if(this.constrain){
17144             var vw = Roo.lib.Dom.getViewWidth(),
17145                 vh = Roo.lib.Dom.getViewHeight();
17146             var s = Roo.get(document).getScroll();
17147
17148             var xy = this.getXY();
17149             var x = xy[0], y = xy[1];   
17150             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17151             // only move it if it needs it
17152             var moved = false;
17153             // first validate right/bottom
17154             if((x + w) > vw+s.left){
17155                 x = vw - w - this.shadowOffset;
17156                 moved = true;
17157             }
17158             if((y + h) > vh+s.top){
17159                 y = vh - h - this.shadowOffset;
17160                 moved = true;
17161             }
17162             // then make sure top/left isn't negative
17163             if(x < s.left){
17164                 x = s.left;
17165                 moved = true;
17166             }
17167             if(y < s.top){
17168                 y = s.top;
17169                 moved = true;
17170             }
17171             if(moved){
17172                 if(this.avoidY){
17173                     var ay = this.avoidY;
17174                     if(y <= ay && (y+h) >= ay){
17175                         y = ay-h-5;   
17176                     }
17177                 }
17178                 xy = [x, y];
17179                 this.storeXY(xy);
17180                 supr.setXY.call(this, xy);
17181                 this.sync();
17182             }
17183         }
17184     },
17185
17186     isVisible : function(){
17187         return this.visible;    
17188     },
17189
17190     // private
17191     showAction : function(){
17192         this.visible = true; // track visibility to prevent getStyle calls
17193         if(this.useDisplay === true){
17194             this.setDisplayed("");
17195         }else if(this.lastXY){
17196             supr.setXY.call(this, this.lastXY);
17197         }else if(this.lastLT){
17198             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17199         }
17200     },
17201
17202     // private
17203     hideAction : function(){
17204         this.visible = false;
17205         if(this.useDisplay === true){
17206             this.setDisplayed(false);
17207         }else{
17208             this.setLeftTop(-10000,-10000);
17209         }
17210     },
17211
17212     // overridden Element method
17213     setVisible : function(v, a, d, c, e){
17214         if(v){
17215             this.showAction();
17216         }
17217         if(a && v){
17218             var cb = function(){
17219                 this.sync(true);
17220                 if(c){
17221                     c();
17222                 }
17223             }.createDelegate(this);
17224             supr.setVisible.call(this, true, true, d, cb, e);
17225         }else{
17226             if(!v){
17227                 this.hideUnders(true);
17228             }
17229             var cb = c;
17230             if(a){
17231                 cb = function(){
17232                     this.hideAction();
17233                     if(c){
17234                         c();
17235                     }
17236                 }.createDelegate(this);
17237             }
17238             supr.setVisible.call(this, v, a, d, cb, e);
17239             if(v){
17240                 this.sync(true);
17241             }else if(!a){
17242                 this.hideAction();
17243             }
17244         }
17245     },
17246
17247     storeXY : function(xy){
17248         delete this.lastLT;
17249         this.lastXY = xy;
17250     },
17251
17252     storeLeftTop : function(left, top){
17253         delete this.lastXY;
17254         this.lastLT = [left, top];
17255     },
17256
17257     // private
17258     beforeFx : function(){
17259         this.beforeAction();
17260         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17261     },
17262
17263     // private
17264     afterFx : function(){
17265         Roo.Layer.superclass.afterFx.apply(this, arguments);
17266         this.sync(this.isVisible());
17267     },
17268
17269     // private
17270     beforeAction : function(){
17271         if(!this.updating && this.shadow){
17272             this.shadow.hide();
17273         }
17274     },
17275
17276     // overridden Element method
17277     setLeft : function(left){
17278         this.storeLeftTop(left, this.getTop(true));
17279         supr.setLeft.apply(this, arguments);
17280         this.sync();
17281     },
17282
17283     setTop : function(top){
17284         this.storeLeftTop(this.getLeft(true), top);
17285         supr.setTop.apply(this, arguments);
17286         this.sync();
17287     },
17288
17289     setLeftTop : function(left, top){
17290         this.storeLeftTop(left, top);
17291         supr.setLeftTop.apply(this, arguments);
17292         this.sync();
17293     },
17294
17295     setXY : function(xy, a, d, c, e){
17296         this.fixDisplay();
17297         this.beforeAction();
17298         this.storeXY(xy);
17299         var cb = this.createCB(c);
17300         supr.setXY.call(this, xy, a, d, cb, e);
17301         if(!a){
17302             cb();
17303         }
17304     },
17305
17306     // private
17307     createCB : function(c){
17308         var el = this;
17309         return function(){
17310             el.constrainXY();
17311             el.sync(true);
17312             if(c){
17313                 c();
17314             }
17315         };
17316     },
17317
17318     // overridden Element method
17319     setX : function(x, a, d, c, e){
17320         this.setXY([x, this.getY()], a, d, c, e);
17321     },
17322
17323     // overridden Element method
17324     setY : function(y, a, d, c, e){
17325         this.setXY([this.getX(), y], a, d, c, e);
17326     },
17327
17328     // overridden Element method
17329     setSize : function(w, h, a, d, c, e){
17330         this.beforeAction();
17331         var cb = this.createCB(c);
17332         supr.setSize.call(this, w, h, a, d, cb, e);
17333         if(!a){
17334             cb();
17335         }
17336     },
17337
17338     // overridden Element method
17339     setWidth : function(w, a, d, c, e){
17340         this.beforeAction();
17341         var cb = this.createCB(c);
17342         supr.setWidth.call(this, w, a, d, cb, e);
17343         if(!a){
17344             cb();
17345         }
17346     },
17347
17348     // overridden Element method
17349     setHeight : function(h, a, d, c, e){
17350         this.beforeAction();
17351         var cb = this.createCB(c);
17352         supr.setHeight.call(this, h, a, d, cb, e);
17353         if(!a){
17354             cb();
17355         }
17356     },
17357
17358     // overridden Element method
17359     setBounds : function(x, y, w, h, a, d, c, e){
17360         this.beforeAction();
17361         var cb = this.createCB(c);
17362         if(!a){
17363             this.storeXY([x, y]);
17364             supr.setXY.call(this, [x, y]);
17365             supr.setSize.call(this, w, h, a, d, cb, e);
17366             cb();
17367         }else{
17368             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17369         }
17370         return this;
17371     },
17372     
17373     /**
17374      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17375      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17376      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17377      * @param {Number} zindex The new z-index to set
17378      * @return {this} The Layer
17379      */
17380     setZIndex : function(zindex){
17381         this.zindex = zindex;
17382         this.setStyle("z-index", zindex + 2);
17383         if(this.shadow){
17384             this.shadow.setZIndex(zindex + 1);
17385         }
17386         if(this.shim){
17387             this.shim.setStyle("z-index", zindex);
17388         }
17389     }
17390 });
17391 })();/*
17392  * Original code for Roojs - LGPL
17393  * <script type="text/javascript">
17394  */
17395  
17396 /**
17397  * @class Roo.XComponent
17398  * A delayed Element creator...
17399  * Or a way to group chunks of interface together.
17400  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17401  *  used in conjunction with XComponent.build() it will create an instance of each element,
17402  *  then call addxtype() to build the User interface.
17403  * 
17404  * Mypart.xyx = new Roo.XComponent({
17405
17406     parent : 'Mypart.xyz', // empty == document.element.!!
17407     order : '001',
17408     name : 'xxxx'
17409     region : 'xxxx'
17410     disabled : function() {} 
17411      
17412     tree : function() { // return an tree of xtype declared components
17413         var MODULE = this;
17414         return 
17415         {
17416             xtype : 'NestedLayoutPanel',
17417             // technicall
17418         }
17419      ]
17420  *})
17421  *
17422  *
17423  * It can be used to build a big heiracy, with parent etc.
17424  * or you can just use this to render a single compoent to a dom element
17425  * MYPART.render(Roo.Element | String(id) | dom_element )
17426  *
17427  *
17428  * Usage patterns.
17429  *
17430  * Classic Roo
17431  *
17432  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17433  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17434  *
17435  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17436  *
17437  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17438  * - if mulitple topModules exist, the last one is defined as the top module.
17439  *
17440  * Embeded Roo
17441  * 
17442  * When the top level or multiple modules are to embedded into a existing HTML page,
17443  * the parent element can container '#id' of the element where the module will be drawn.
17444  *
17445  * Bootstrap Roo
17446  *
17447  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17448  * it relies more on a include mechanism, where sub modules are included into an outer page.
17449  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17450  * 
17451  * Bootstrap Roo Included elements
17452  *
17453  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17454  * hence confusing the component builder as it thinks there are multiple top level elements. 
17455  *
17456  * String Over-ride & Translations
17457  *
17458  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17459  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17460  * are needed. @see Roo.XComponent.overlayString  
17461  * 
17462  * 
17463  * 
17464  * @extends Roo.util.Observable
17465  * @constructor
17466  * @param cfg {Object} configuration of component
17467  * 
17468  */
17469 Roo.XComponent = function(cfg) {
17470     Roo.apply(this, cfg);
17471     this.addEvents({ 
17472         /**
17473              * @event built
17474              * Fires when this the componnt is built
17475              * @param {Roo.XComponent} c the component
17476              */
17477         'built' : true
17478         
17479     });
17480     this.region = this.region || 'center'; // default..
17481     Roo.XComponent.register(this);
17482     this.modules = false;
17483     this.el = false; // where the layout goes..
17484     
17485     
17486 }
17487 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17488     /**
17489      * @property el
17490      * The created element (with Roo.factory())
17491      * @type {Roo.Layout}
17492      */
17493     el  : false,
17494     
17495     /**
17496      * @property el
17497      * for BC  - use el in new code
17498      * @type {Roo.Layout}
17499      */
17500     panel : false,
17501     
17502     /**
17503      * @property layout
17504      * for BC  - use el in new code
17505      * @type {Roo.Layout}
17506      */
17507     layout : false,
17508     
17509      /**
17510      * @cfg {Function|boolean} disabled
17511      * If this module is disabled by some rule, return true from the funtion
17512      */
17513     disabled : false,
17514     
17515     /**
17516      * @cfg {String} parent 
17517      * Name of parent element which it get xtype added to..
17518      */
17519     parent: false,
17520     
17521     /**
17522      * @cfg {String} order
17523      * Used to set the order in which elements are created (usefull for multiple tabs)
17524      */
17525     
17526     order : false,
17527     /**
17528      * @cfg {String} name
17529      * String to display while loading.
17530      */
17531     name : false,
17532     /**
17533      * @cfg {String} region
17534      * Region to render component to (defaults to center)
17535      */
17536     region : 'center',
17537     
17538     /**
17539      * @cfg {Array} items
17540      * A single item array - the first element is the root of the tree..
17541      * It's done this way to stay compatible with the Xtype system...
17542      */
17543     items : false,
17544     
17545     /**
17546      * @property _tree
17547      * The method that retuns the tree of parts that make up this compoennt 
17548      * @type {function}
17549      */
17550     _tree  : false,
17551     
17552      /**
17553      * render
17554      * render element to dom or tree
17555      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17556      */
17557     
17558     render : function(el)
17559     {
17560         
17561         el = el || false;
17562         var hp = this.parent ? 1 : 0;
17563         Roo.debug &&  Roo.log(this);
17564         
17565         var tree = this._tree ? this._tree() : this.tree();
17566
17567         
17568         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17569             // if parent is a '#.....' string, then let's use that..
17570             var ename = this.parent.substr(1);
17571             this.parent = false;
17572             Roo.debug && Roo.log(ename);
17573             switch (ename) {
17574                 case 'bootstrap-body':
17575                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17576                         // this is the BorderLayout standard?
17577                        this.parent = { el : true };
17578                        break;
17579                     }
17580                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17581                         // need to insert stuff...
17582                         this.parent =  {
17583                              el : new Roo.bootstrap.layout.Border({
17584                                  el : document.body, 
17585                      
17586                                  center: {
17587                                     titlebar: false,
17588                                     autoScroll:false,
17589                                     closeOnTab: true,
17590                                     tabPosition: 'top',
17591                                       //resizeTabs: true,
17592                                     alwaysShowTabs: true,
17593                                     hideTabs: false
17594                                      //minTabWidth: 140
17595                                  }
17596                              })
17597                         
17598                          };
17599                          break;
17600                     }
17601                          
17602                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17603                         this.parent = { el :  new  Roo.bootstrap.Body() };
17604                         Roo.debug && Roo.log("setting el to doc body");
17605                          
17606                     } else {
17607                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17608                     }
17609                     break;
17610                 case 'bootstrap':
17611                     this.parent = { el : true};
17612                     // fall through
17613                 default:
17614                     el = Roo.get(ename);
17615                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17616                         this.parent = { el : true};
17617                     }
17618                     
17619                     break;
17620             }
17621                 
17622             
17623             if (!el && !this.parent) {
17624                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17625                 return;
17626             }
17627         }
17628         
17629         Roo.debug && Roo.log("EL:");
17630         Roo.debug && Roo.log(el);
17631         Roo.debug && Roo.log("this.parent.el:");
17632         Roo.debug && Roo.log(this.parent.el);
17633         
17634
17635         // altertive root elements ??? - we need a better way to indicate these.
17636         var is_alt = Roo.XComponent.is_alt ||
17637                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17638                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17639                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17640         
17641         
17642         
17643         if (!this.parent && is_alt) {
17644             //el = Roo.get(document.body);
17645             this.parent = { el : true };
17646         }
17647             
17648             
17649         
17650         if (!this.parent) {
17651             
17652             Roo.debug && Roo.log("no parent - creating one");
17653             
17654             el = el ? Roo.get(el) : false;      
17655             
17656             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17657                 
17658                 this.parent =  {
17659                     el : new Roo.bootstrap.layout.Border({
17660                         el: el || document.body,
17661                     
17662                         center: {
17663                             titlebar: false,
17664                             autoScroll:false,
17665                             closeOnTab: true,
17666                             tabPosition: 'top',
17667                              //resizeTabs: true,
17668                             alwaysShowTabs: false,
17669                             hideTabs: true,
17670                             minTabWidth: 140,
17671                             overflow: 'visible'
17672                          }
17673                      })
17674                 };
17675             } else {
17676             
17677                 // it's a top level one..
17678                 this.parent =  {
17679                     el : new Roo.BorderLayout(el || document.body, {
17680                         center: {
17681                             titlebar: false,
17682                             autoScroll:false,
17683                             closeOnTab: true,
17684                             tabPosition: 'top',
17685                              //resizeTabs: true,
17686                             alwaysShowTabs: el && hp? false :  true,
17687                             hideTabs: el || !hp ? true :  false,
17688                             minTabWidth: 140
17689                          }
17690                     })
17691                 };
17692             }
17693         }
17694         
17695         if (!this.parent.el) {
17696                 // probably an old style ctor, which has been disabled.
17697                 return;
17698
17699         }
17700                 // The 'tree' method is  '_tree now' 
17701             
17702         tree.region = tree.region || this.region;
17703         var is_body = false;
17704         if (this.parent.el === true) {
17705             // bootstrap... - body..
17706             if (el) {
17707                 tree.el = el;
17708             }
17709             this.parent.el = Roo.factory(tree);
17710             is_body = true;
17711         }
17712         
17713         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17714         this.fireEvent('built', this);
17715         
17716         this.panel = this.el;
17717         this.layout = this.panel.layout;
17718         this.parentLayout = this.parent.layout  || false;  
17719          
17720     }
17721     
17722 });
17723
17724 Roo.apply(Roo.XComponent, {
17725     /**
17726      * @property  hideProgress
17727      * true to disable the building progress bar.. usefull on single page renders.
17728      * @type Boolean
17729      */
17730     hideProgress : false,
17731     /**
17732      * @property  buildCompleted
17733      * True when the builder has completed building the interface.
17734      * @type Boolean
17735      */
17736     buildCompleted : false,
17737      
17738     /**
17739      * @property  topModule
17740      * the upper most module - uses document.element as it's constructor.
17741      * @type Object
17742      */
17743      
17744     topModule  : false,
17745       
17746     /**
17747      * @property  modules
17748      * array of modules to be created by registration system.
17749      * @type {Array} of Roo.XComponent
17750      */
17751     
17752     modules : [],
17753     /**
17754      * @property  elmodules
17755      * array of modules to be created by which use #ID 
17756      * @type {Array} of Roo.XComponent
17757      */
17758      
17759     elmodules : [],
17760
17761      /**
17762      * @property  is_alt
17763      * Is an alternative Root - normally used by bootstrap or other systems,
17764      *    where the top element in the tree can wrap 'body' 
17765      * @type {boolean}  (default false)
17766      */
17767      
17768     is_alt : false,
17769     /**
17770      * @property  build_from_html
17771      * Build elements from html - used by bootstrap HTML stuff 
17772      *    - this is cleared after build is completed
17773      * @type {boolean}    (default false)
17774      */
17775      
17776     build_from_html : false,
17777     /**
17778      * Register components to be built later.
17779      *
17780      * This solves the following issues
17781      * - Building is not done on page load, but after an authentication process has occured.
17782      * - Interface elements are registered on page load
17783      * - Parent Interface elements may not be loaded before child, so this handles that..
17784      * 
17785      *
17786      * example:
17787      * 
17788      * MyApp.register({
17789           order : '000001',
17790           module : 'Pman.Tab.projectMgr',
17791           region : 'center',
17792           parent : 'Pman.layout',
17793           disabled : false,  // or use a function..
17794         })
17795      
17796      * * @param {Object} details about module
17797      */
17798     register : function(obj) {
17799                 
17800         Roo.XComponent.event.fireEvent('register', obj);
17801         switch(typeof(obj.disabled) ) {
17802                 
17803             case 'undefined':
17804                 break;
17805             
17806             case 'function':
17807                 if ( obj.disabled() ) {
17808                         return;
17809                 }
17810                 break;
17811             
17812             default:
17813                 if (obj.disabled || obj.region == '#disabled') {
17814                         return;
17815                 }
17816                 break;
17817         }
17818                 
17819         this.modules.push(obj);
17820          
17821     },
17822     /**
17823      * convert a string to an object..
17824      * eg. 'AAA.BBB' -> finds AAA.BBB
17825
17826      */
17827     
17828     toObject : function(str)
17829     {
17830         if (!str || typeof(str) == 'object') {
17831             return str;
17832         }
17833         if (str.substring(0,1) == '#') {
17834             return str;
17835         }
17836
17837         var ar = str.split('.');
17838         var rt, o;
17839         rt = ar.shift();
17840             /** eval:var:o */
17841         try {
17842             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17843         } catch (e) {
17844             throw "Module not found : " + str;
17845         }
17846         
17847         if (o === false) {
17848             throw "Module not found : " + str;
17849         }
17850         Roo.each(ar, function(e) {
17851             if (typeof(o[e]) == 'undefined') {
17852                 throw "Module not found : " + str;
17853             }
17854             o = o[e];
17855         });
17856         
17857         return o;
17858         
17859     },
17860     
17861     
17862     /**
17863      * move modules into their correct place in the tree..
17864      * 
17865      */
17866     preBuild : function ()
17867     {
17868         var _t = this;
17869         Roo.each(this.modules , function (obj)
17870         {
17871             Roo.XComponent.event.fireEvent('beforebuild', obj);
17872             
17873             var opar = obj.parent;
17874             try { 
17875                 obj.parent = this.toObject(opar);
17876             } catch(e) {
17877                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17878                 return;
17879             }
17880             
17881             if (!obj.parent) {
17882                 Roo.debug && Roo.log("GOT top level module");
17883                 Roo.debug && Roo.log(obj);
17884                 obj.modules = new Roo.util.MixedCollection(false, 
17885                     function(o) { return o.order + '' }
17886                 );
17887                 this.topModule = obj;
17888                 return;
17889             }
17890                         // parent is a string (usually a dom element name..)
17891             if (typeof(obj.parent) == 'string') {
17892                 this.elmodules.push(obj);
17893                 return;
17894             }
17895             if (obj.parent.constructor != Roo.XComponent) {
17896                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17897             }
17898             if (!obj.parent.modules) {
17899                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17900                     function(o) { return o.order + '' }
17901                 );
17902             }
17903             if (obj.parent.disabled) {
17904                 obj.disabled = true;
17905             }
17906             obj.parent.modules.add(obj);
17907         }, this);
17908     },
17909     
17910      /**
17911      * make a list of modules to build.
17912      * @return {Array} list of modules. 
17913      */ 
17914     
17915     buildOrder : function()
17916     {
17917         var _this = this;
17918         var cmp = function(a,b) {   
17919             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17920         };
17921         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17922             throw "No top level modules to build";
17923         }
17924         
17925         // make a flat list in order of modules to build.
17926         var mods = this.topModule ? [ this.topModule ] : [];
17927                 
17928         
17929         // elmodules (is a list of DOM based modules )
17930         Roo.each(this.elmodules, function(e) {
17931             mods.push(e);
17932             if (!this.topModule &&
17933                 typeof(e.parent) == 'string' &&
17934                 e.parent.substring(0,1) == '#' &&
17935                 Roo.get(e.parent.substr(1))
17936                ) {
17937                 
17938                 _this.topModule = e;
17939             }
17940             
17941         });
17942
17943         
17944         // add modules to their parents..
17945         var addMod = function(m) {
17946             Roo.debug && Roo.log("build Order: add: " + m.name);
17947                 
17948             mods.push(m);
17949             if (m.modules && !m.disabled) {
17950                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17951                 m.modules.keySort('ASC',  cmp );
17952                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17953     
17954                 m.modules.each(addMod);
17955             } else {
17956                 Roo.debug && Roo.log("build Order: no child modules");
17957             }
17958             // not sure if this is used any more..
17959             if (m.finalize) {
17960                 m.finalize.name = m.name + " (clean up) ";
17961                 mods.push(m.finalize);
17962             }
17963             
17964         }
17965         if (this.topModule && this.topModule.modules) { 
17966             this.topModule.modules.keySort('ASC',  cmp );
17967             this.topModule.modules.each(addMod);
17968         } 
17969         return mods;
17970     },
17971     
17972      /**
17973      * Build the registered modules.
17974      * @param {Object} parent element.
17975      * @param {Function} optional method to call after module has been added.
17976      * 
17977      */ 
17978    
17979     build : function(opts) 
17980     {
17981         
17982         if (typeof(opts) != 'undefined') {
17983             Roo.apply(this,opts);
17984         }
17985         
17986         this.preBuild();
17987         var mods = this.buildOrder();
17988       
17989         //this.allmods = mods;
17990         //Roo.debug && Roo.log(mods);
17991         //return;
17992         if (!mods.length) { // should not happen
17993             throw "NO modules!!!";
17994         }
17995         
17996         
17997         var msg = "Building Interface...";
17998         // flash it up as modal - so we store the mask!?
17999         if (!this.hideProgress && Roo.MessageBox) {
18000             Roo.MessageBox.show({ title: 'loading' });
18001             Roo.MessageBox.show({
18002                title: "Please wait...",
18003                msg: msg,
18004                width:450,
18005                progress:true,
18006                buttons : false,
18007                closable:false,
18008                modal: false
18009               
18010             });
18011         }
18012         var total = mods.length;
18013         
18014         var _this = this;
18015         var progressRun = function() {
18016             if (!mods.length) {
18017                 Roo.debug && Roo.log('hide?');
18018                 if (!this.hideProgress && Roo.MessageBox) {
18019                     Roo.MessageBox.hide();
18020                 }
18021                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18022                 
18023                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18024                 
18025                 // THE END...
18026                 return false;   
18027             }
18028             
18029             var m = mods.shift();
18030             
18031             
18032             Roo.debug && Roo.log(m);
18033             // not sure if this is supported any more.. - modules that are are just function
18034             if (typeof(m) == 'function') { 
18035                 m.call(this);
18036                 return progressRun.defer(10, _this);
18037             } 
18038             
18039             
18040             msg = "Building Interface " + (total  - mods.length) + 
18041                     " of " + total + 
18042                     (m.name ? (' - ' + m.name) : '');
18043                         Roo.debug && Roo.log(msg);
18044             if (!_this.hideProgress &&  Roo.MessageBox) { 
18045                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18046             }
18047             
18048          
18049             // is the module disabled?
18050             var disabled = (typeof(m.disabled) == 'function') ?
18051                 m.disabled.call(m.module.disabled) : m.disabled;    
18052             
18053             
18054             if (disabled) {
18055                 return progressRun(); // we do not update the display!
18056             }
18057             
18058             // now build 
18059             
18060                         
18061                         
18062             m.render();
18063             // it's 10 on top level, and 1 on others??? why...
18064             return progressRun.defer(10, _this);
18065              
18066         }
18067         progressRun.defer(1, _this);
18068      
18069         
18070         
18071     },
18072     /**
18073      * Overlay a set of modified strings onto a component
18074      * This is dependant on our builder exporting the strings and 'named strings' elements.
18075      * 
18076      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18077      * @param {Object} associative array of 'named' string and it's new value.
18078      * 
18079      */
18080         overlayStrings : function( component, strings )
18081     {
18082         if (typeof(component['_named_strings']) == 'undefined') {
18083             throw "ERROR: component does not have _named_strings";
18084         }
18085         for ( var k in strings ) {
18086             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18087             if (md !== false) {
18088                 component['_strings'][md] = strings[k];
18089             } else {
18090                 Roo.log('could not find named string: ' + k + ' in');
18091                 Roo.log(component);
18092             }
18093             
18094         }
18095         
18096     },
18097     
18098         
18099         /**
18100          * Event Object.
18101          *
18102          *
18103          */
18104         event: false, 
18105     /**
18106          * wrapper for event.on - aliased later..  
18107          * Typically use to register a event handler for register:
18108          *
18109          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18110          *
18111          */
18112     on : false
18113    
18114     
18115     
18116 });
18117
18118 Roo.XComponent.event = new Roo.util.Observable({
18119                 events : { 
18120                         /**
18121                          * @event register
18122                          * Fires when an Component is registered,
18123                          * set the disable property on the Component to stop registration.
18124                          * @param {Roo.XComponent} c the component being registerd.
18125                          * 
18126                          */
18127                         'register' : true,
18128             /**
18129                          * @event beforebuild
18130                          * Fires before each Component is built
18131                          * can be used to apply permissions.
18132                          * @param {Roo.XComponent} c the component being registerd.
18133                          * 
18134                          */
18135                         'beforebuild' : true,
18136                         /**
18137                          * @event buildcomplete
18138                          * Fires on the top level element when all elements have been built
18139                          * @param {Roo.XComponent} the top level component.
18140                          */
18141                         'buildcomplete' : true
18142                         
18143                 }
18144 });
18145
18146 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18147  //
18148  /**
18149  * marked - a markdown parser
18150  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18151  * https://github.com/chjj/marked
18152  */
18153
18154
18155 /**
18156  *
18157  * Roo.Markdown - is a very crude wrapper around marked..
18158  *
18159  * usage:
18160  * 
18161  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18162  * 
18163  * Note: move the sample code to the bottom of this
18164  * file before uncommenting it.
18165  *
18166  */
18167
18168 Roo.Markdown = {};
18169 Roo.Markdown.toHtml = function(text) {
18170     
18171     var c = new Roo.Markdown.marked.setOptions({
18172             renderer: new Roo.Markdown.marked.Renderer(),
18173             gfm: true,
18174             tables: true,
18175             breaks: false,
18176             pedantic: false,
18177             sanitize: false,
18178             smartLists: true,
18179             smartypants: false
18180           });
18181     // A FEW HACKS!!?
18182     
18183     text = text.replace(/\\\n/g,' ');
18184     return Roo.Markdown.marked(text);
18185 };
18186 //
18187 // converter
18188 //
18189 // Wraps all "globals" so that the only thing
18190 // exposed is makeHtml().
18191 //
18192 (function() {
18193     
18194      /**
18195          * eval:var:escape
18196          * eval:var:unescape
18197          * eval:var:replace
18198          */
18199       
18200     /**
18201      * Helpers
18202      */
18203     
18204     var escape = function (html, encode) {
18205       return html
18206         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18207         .replace(/</g, '&lt;')
18208         .replace(/>/g, '&gt;')
18209         .replace(/"/g, '&quot;')
18210         .replace(/'/g, '&#39;');
18211     }
18212     
18213     var unescape = function (html) {
18214         // explicitly match decimal, hex, and named HTML entities 
18215       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18216         n = n.toLowerCase();
18217         if (n === 'colon') { return ':'; }
18218         if (n.charAt(0) === '#') {
18219           return n.charAt(1) === 'x'
18220             ? String.fromCharCode(parseInt(n.substring(2), 16))
18221             : String.fromCharCode(+n.substring(1));
18222         }
18223         return '';
18224       });
18225     }
18226     
18227     var replace = function (regex, opt) {
18228       regex = regex.source;
18229       opt = opt || '';
18230       return function self(name, val) {
18231         if (!name) { return new RegExp(regex, opt); }
18232         val = val.source || val;
18233         val = val.replace(/(^|[^\[])\^/g, '$1');
18234         regex = regex.replace(name, val);
18235         return self;
18236       };
18237     }
18238
18239
18240          /**
18241          * eval:var:noop
18242     */
18243     var noop = function () {}
18244     noop.exec = noop;
18245     
18246          /**
18247          * eval:var:merge
18248     */
18249     var merge = function (obj) {
18250       var i = 1
18251         , target
18252         , key;
18253     
18254       for (; i < arguments.length; i++) {
18255         target = arguments[i];
18256         for (key in target) {
18257           if (Object.prototype.hasOwnProperty.call(target, key)) {
18258             obj[key] = target[key];
18259           }
18260         }
18261       }
18262     
18263       return obj;
18264     }
18265     
18266     
18267     /**
18268      * Block-Level Grammar
18269      */
18270     
18271     
18272     
18273     
18274     var block = {
18275       newline: /^\n+/,
18276       code: /^( {4}[^\n]+\n*)+/,
18277       fences: noop,
18278       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18279       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18280       nptable: noop,
18281       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18282       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18283       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18284       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18285       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18286       table: noop,
18287       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18288       text: /^[^\n]+/
18289     };
18290     
18291     block.bullet = /(?:[*+-]|\d+\.)/;
18292     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18293     block.item = replace(block.item, 'gm')
18294       (/bull/g, block.bullet)
18295       ();
18296     
18297     block.list = replace(block.list)
18298       (/bull/g, block.bullet)
18299       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18300       ('def', '\\n+(?=' + block.def.source + ')')
18301       ();
18302     
18303     block.blockquote = replace(block.blockquote)
18304       ('def', block.def)
18305       ();
18306     
18307     block._tag = '(?!(?:'
18308       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18309       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18310       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18311     
18312     block.html = replace(block.html)
18313       ('comment', /<!--[\s\S]*?-->/)
18314       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18315       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18316       (/tag/g, block._tag)
18317       ();
18318     
18319     block.paragraph = replace(block.paragraph)
18320       ('hr', block.hr)
18321       ('heading', block.heading)
18322       ('lheading', block.lheading)
18323       ('blockquote', block.blockquote)
18324       ('tag', '<' + block._tag)
18325       ('def', block.def)
18326       ();
18327     
18328     /**
18329      * Normal Block Grammar
18330      */
18331     
18332     block.normal = merge({}, block);
18333     
18334     /**
18335      * GFM Block Grammar
18336      */
18337     
18338     block.gfm = merge({}, block.normal, {
18339       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18340       paragraph: /^/,
18341       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18342     });
18343     
18344     block.gfm.paragraph = replace(block.paragraph)
18345       ('(?!', '(?!'
18346         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18347         + block.list.source.replace('\\1', '\\3') + '|')
18348       ();
18349     
18350     /**
18351      * GFM + Tables Block Grammar
18352      */
18353     
18354     block.tables = merge({}, block.gfm, {
18355       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18356       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18357     });
18358     
18359     /**
18360      * Block Lexer
18361      */
18362     
18363     var Lexer = function (options) {
18364       this.tokens = [];
18365       this.tokens.links = {};
18366       this.options = options || marked.defaults;
18367       this.rules = block.normal;
18368     
18369       if (this.options.gfm) {
18370         if (this.options.tables) {
18371           this.rules = block.tables;
18372         } else {
18373           this.rules = block.gfm;
18374         }
18375       }
18376     }
18377     
18378     /**
18379      * Expose Block Rules
18380      */
18381     
18382     Lexer.rules = block;
18383     
18384     /**
18385      * Static Lex Method
18386      */
18387     
18388     Lexer.lex = function(src, options) {
18389       var lexer = new Lexer(options);
18390       return lexer.lex(src);
18391     };
18392     
18393     /**
18394      * Preprocessing
18395      */
18396     
18397     Lexer.prototype.lex = function(src) {
18398       src = src
18399         .replace(/\r\n|\r/g, '\n')
18400         .replace(/\t/g, '    ')
18401         .replace(/\u00a0/g, ' ')
18402         .replace(/\u2424/g, '\n');
18403     
18404       return this.token(src, true);
18405     };
18406     
18407     /**
18408      * Lexing
18409      */
18410     
18411     Lexer.prototype.token = function(src, top, bq) {
18412       var src = src.replace(/^ +$/gm, '')
18413         , next
18414         , loose
18415         , cap
18416         , bull
18417         , b
18418         , item
18419         , space
18420         , i
18421         , l;
18422     
18423       while (src) {
18424         // newline
18425         if (cap = this.rules.newline.exec(src)) {
18426           src = src.substring(cap[0].length);
18427           if (cap[0].length > 1) {
18428             this.tokens.push({
18429               type: 'space'
18430             });
18431           }
18432         }
18433     
18434         // code
18435         if (cap = this.rules.code.exec(src)) {
18436           src = src.substring(cap[0].length);
18437           cap = cap[0].replace(/^ {4}/gm, '');
18438           this.tokens.push({
18439             type: 'code',
18440             text: !this.options.pedantic
18441               ? cap.replace(/\n+$/, '')
18442               : cap
18443           });
18444           continue;
18445         }
18446     
18447         // fences (gfm)
18448         if (cap = this.rules.fences.exec(src)) {
18449           src = src.substring(cap[0].length);
18450           this.tokens.push({
18451             type: 'code',
18452             lang: cap[2],
18453             text: cap[3] || ''
18454           });
18455           continue;
18456         }
18457     
18458         // heading
18459         if (cap = this.rules.heading.exec(src)) {
18460           src = src.substring(cap[0].length);
18461           this.tokens.push({
18462             type: 'heading',
18463             depth: cap[1].length,
18464             text: cap[2]
18465           });
18466           continue;
18467         }
18468     
18469         // table no leading pipe (gfm)
18470         if (top && (cap = this.rules.nptable.exec(src))) {
18471           src = src.substring(cap[0].length);
18472     
18473           item = {
18474             type: 'table',
18475             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18476             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18477             cells: cap[3].replace(/\n$/, '').split('\n')
18478           };
18479     
18480           for (i = 0; i < item.align.length; i++) {
18481             if (/^ *-+: *$/.test(item.align[i])) {
18482               item.align[i] = 'right';
18483             } else if (/^ *:-+: *$/.test(item.align[i])) {
18484               item.align[i] = 'center';
18485             } else if (/^ *:-+ *$/.test(item.align[i])) {
18486               item.align[i] = 'left';
18487             } else {
18488               item.align[i] = null;
18489             }
18490           }
18491     
18492           for (i = 0; i < item.cells.length; i++) {
18493             item.cells[i] = item.cells[i].split(/ *\| */);
18494           }
18495     
18496           this.tokens.push(item);
18497     
18498           continue;
18499         }
18500     
18501         // lheading
18502         if (cap = this.rules.lheading.exec(src)) {
18503           src = src.substring(cap[0].length);
18504           this.tokens.push({
18505             type: 'heading',
18506             depth: cap[2] === '=' ? 1 : 2,
18507             text: cap[1]
18508           });
18509           continue;
18510         }
18511     
18512         // hr
18513         if (cap = this.rules.hr.exec(src)) {
18514           src = src.substring(cap[0].length);
18515           this.tokens.push({
18516             type: 'hr'
18517           });
18518           continue;
18519         }
18520     
18521         // blockquote
18522         if (cap = this.rules.blockquote.exec(src)) {
18523           src = src.substring(cap[0].length);
18524     
18525           this.tokens.push({
18526             type: 'blockquote_start'
18527           });
18528     
18529           cap = cap[0].replace(/^ *> ?/gm, '');
18530     
18531           // Pass `top` to keep the current
18532           // "toplevel" state. This is exactly
18533           // how markdown.pl works.
18534           this.token(cap, top, true);
18535     
18536           this.tokens.push({
18537             type: 'blockquote_end'
18538           });
18539     
18540           continue;
18541         }
18542     
18543         // list
18544         if (cap = this.rules.list.exec(src)) {
18545           src = src.substring(cap[0].length);
18546           bull = cap[2];
18547     
18548           this.tokens.push({
18549             type: 'list_start',
18550             ordered: bull.length > 1
18551           });
18552     
18553           // Get each top-level item.
18554           cap = cap[0].match(this.rules.item);
18555     
18556           next = false;
18557           l = cap.length;
18558           i = 0;
18559     
18560           for (; i < l; i++) {
18561             item = cap[i];
18562     
18563             // Remove the list item's bullet
18564             // so it is seen as the next token.
18565             space = item.length;
18566             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18567     
18568             // Outdent whatever the
18569             // list item contains. Hacky.
18570             if (~item.indexOf('\n ')) {
18571               space -= item.length;
18572               item = !this.options.pedantic
18573                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18574                 : item.replace(/^ {1,4}/gm, '');
18575             }
18576     
18577             // Determine whether the next list item belongs here.
18578             // Backpedal if it does not belong in this list.
18579             if (this.options.smartLists && i !== l - 1) {
18580               b = block.bullet.exec(cap[i + 1])[0];
18581               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18582                 src = cap.slice(i + 1).join('\n') + src;
18583                 i = l - 1;
18584               }
18585             }
18586     
18587             // Determine whether item is loose or not.
18588             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18589             // for discount behavior.
18590             loose = next || /\n\n(?!\s*$)/.test(item);
18591             if (i !== l - 1) {
18592               next = item.charAt(item.length - 1) === '\n';
18593               if (!loose) { loose = next; }
18594             }
18595     
18596             this.tokens.push({
18597               type: loose
18598                 ? 'loose_item_start'
18599                 : 'list_item_start'
18600             });
18601     
18602             // Recurse.
18603             this.token(item, false, bq);
18604     
18605             this.tokens.push({
18606               type: 'list_item_end'
18607             });
18608           }
18609     
18610           this.tokens.push({
18611             type: 'list_end'
18612           });
18613     
18614           continue;
18615         }
18616     
18617         // html
18618         if (cap = this.rules.html.exec(src)) {
18619           src = src.substring(cap[0].length);
18620           this.tokens.push({
18621             type: this.options.sanitize
18622               ? 'paragraph'
18623               : 'html',
18624             pre: !this.options.sanitizer
18625               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18626             text: cap[0]
18627           });
18628           continue;
18629         }
18630     
18631         // def
18632         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18633           src = src.substring(cap[0].length);
18634           this.tokens.links[cap[1].toLowerCase()] = {
18635             href: cap[2],
18636             title: cap[3]
18637           };
18638           continue;
18639         }
18640     
18641         // table (gfm)
18642         if (top && (cap = this.rules.table.exec(src))) {
18643           src = src.substring(cap[0].length);
18644     
18645           item = {
18646             type: 'table',
18647             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18648             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18649             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18650           };
18651     
18652           for (i = 0; i < item.align.length; i++) {
18653             if (/^ *-+: *$/.test(item.align[i])) {
18654               item.align[i] = 'right';
18655             } else if (/^ *:-+: *$/.test(item.align[i])) {
18656               item.align[i] = 'center';
18657             } else if (/^ *:-+ *$/.test(item.align[i])) {
18658               item.align[i] = 'left';
18659             } else {
18660               item.align[i] = null;
18661             }
18662           }
18663     
18664           for (i = 0; i < item.cells.length; i++) {
18665             item.cells[i] = item.cells[i]
18666               .replace(/^ *\| *| *\| *$/g, '')
18667               .split(/ *\| */);
18668           }
18669     
18670           this.tokens.push(item);
18671     
18672           continue;
18673         }
18674     
18675         // top-level paragraph
18676         if (top && (cap = this.rules.paragraph.exec(src))) {
18677           src = src.substring(cap[0].length);
18678           this.tokens.push({
18679             type: 'paragraph',
18680             text: cap[1].charAt(cap[1].length - 1) === '\n'
18681               ? cap[1].slice(0, -1)
18682               : cap[1]
18683           });
18684           continue;
18685         }
18686     
18687         // text
18688         if (cap = this.rules.text.exec(src)) {
18689           // Top-level should never reach here.
18690           src = src.substring(cap[0].length);
18691           this.tokens.push({
18692             type: 'text',
18693             text: cap[0]
18694           });
18695           continue;
18696         }
18697     
18698         if (src) {
18699           throw new
18700             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18701         }
18702       }
18703     
18704       return this.tokens;
18705     };
18706     
18707     /**
18708      * Inline-Level Grammar
18709      */
18710     
18711     var inline = {
18712       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18713       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18714       url: noop,
18715       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18716       link: /^!?\[(inside)\]\(href\)/,
18717       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18718       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18719       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18720       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18721       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18722       br: /^ {2,}\n(?!\s*$)/,
18723       del: noop,
18724       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18725     };
18726     
18727     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18728     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18729     
18730     inline.link = replace(inline.link)
18731       ('inside', inline._inside)
18732       ('href', inline._href)
18733       ();
18734     
18735     inline.reflink = replace(inline.reflink)
18736       ('inside', inline._inside)
18737       ();
18738     
18739     /**
18740      * Normal Inline Grammar
18741      */
18742     
18743     inline.normal = merge({}, inline);
18744     
18745     /**
18746      * Pedantic Inline Grammar
18747      */
18748     
18749     inline.pedantic = merge({}, inline.normal, {
18750       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18751       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18752     });
18753     
18754     /**
18755      * GFM Inline Grammar
18756      */
18757     
18758     inline.gfm = merge({}, inline.normal, {
18759       escape: replace(inline.escape)('])', '~|])')(),
18760       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18761       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18762       text: replace(inline.text)
18763         (']|', '~]|')
18764         ('|', '|https?://|')
18765         ()
18766     });
18767     
18768     /**
18769      * GFM + Line Breaks Inline Grammar
18770      */
18771     
18772     inline.breaks = merge({}, inline.gfm, {
18773       br: replace(inline.br)('{2,}', '*')(),
18774       text: replace(inline.gfm.text)('{2,}', '*')()
18775     });
18776     
18777     /**
18778      * Inline Lexer & Compiler
18779      */
18780     
18781     var InlineLexer  = function (links, options) {
18782       this.options = options || marked.defaults;
18783       this.links = links;
18784       this.rules = inline.normal;
18785       this.renderer = this.options.renderer || new Renderer;
18786       this.renderer.options = this.options;
18787     
18788       if (!this.links) {
18789         throw new
18790           Error('Tokens array requires a `links` property.');
18791       }
18792     
18793       if (this.options.gfm) {
18794         if (this.options.breaks) {
18795           this.rules = inline.breaks;
18796         } else {
18797           this.rules = inline.gfm;
18798         }
18799       } else if (this.options.pedantic) {
18800         this.rules = inline.pedantic;
18801       }
18802     }
18803     
18804     /**
18805      * Expose Inline Rules
18806      */
18807     
18808     InlineLexer.rules = inline;
18809     
18810     /**
18811      * Static Lexing/Compiling Method
18812      */
18813     
18814     InlineLexer.output = function(src, links, options) {
18815       var inline = new InlineLexer(links, options);
18816       return inline.output(src);
18817     };
18818     
18819     /**
18820      * Lexing/Compiling
18821      */
18822     
18823     InlineLexer.prototype.output = function(src) {
18824       var out = ''
18825         , link
18826         , text
18827         , href
18828         , cap;
18829     
18830       while (src) {
18831         // escape
18832         if (cap = this.rules.escape.exec(src)) {
18833           src = src.substring(cap[0].length);
18834           out += cap[1];
18835           continue;
18836         }
18837     
18838         // autolink
18839         if (cap = this.rules.autolink.exec(src)) {
18840           src = src.substring(cap[0].length);
18841           if (cap[2] === '@') {
18842             text = cap[1].charAt(6) === ':'
18843               ? this.mangle(cap[1].substring(7))
18844               : this.mangle(cap[1]);
18845             href = this.mangle('mailto:') + text;
18846           } else {
18847             text = escape(cap[1]);
18848             href = text;
18849           }
18850           out += this.renderer.link(href, null, text);
18851           continue;
18852         }
18853     
18854         // url (gfm)
18855         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18856           src = src.substring(cap[0].length);
18857           text = escape(cap[1]);
18858           href = text;
18859           out += this.renderer.link(href, null, text);
18860           continue;
18861         }
18862     
18863         // tag
18864         if (cap = this.rules.tag.exec(src)) {
18865           if (!this.inLink && /^<a /i.test(cap[0])) {
18866             this.inLink = true;
18867           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18868             this.inLink = false;
18869           }
18870           src = src.substring(cap[0].length);
18871           out += this.options.sanitize
18872             ? this.options.sanitizer
18873               ? this.options.sanitizer(cap[0])
18874               : escape(cap[0])
18875             : cap[0];
18876           continue;
18877         }
18878     
18879         // link
18880         if (cap = this.rules.link.exec(src)) {
18881           src = src.substring(cap[0].length);
18882           this.inLink = true;
18883           out += this.outputLink(cap, {
18884             href: cap[2],
18885             title: cap[3]
18886           });
18887           this.inLink = false;
18888           continue;
18889         }
18890     
18891         // reflink, nolink
18892         if ((cap = this.rules.reflink.exec(src))
18893             || (cap = this.rules.nolink.exec(src))) {
18894           src = src.substring(cap[0].length);
18895           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18896           link = this.links[link.toLowerCase()];
18897           if (!link || !link.href) {
18898             out += cap[0].charAt(0);
18899             src = cap[0].substring(1) + src;
18900             continue;
18901           }
18902           this.inLink = true;
18903           out += this.outputLink(cap, link);
18904           this.inLink = false;
18905           continue;
18906         }
18907     
18908         // strong
18909         if (cap = this.rules.strong.exec(src)) {
18910           src = src.substring(cap[0].length);
18911           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18912           continue;
18913         }
18914     
18915         // em
18916         if (cap = this.rules.em.exec(src)) {
18917           src = src.substring(cap[0].length);
18918           out += this.renderer.em(this.output(cap[2] || cap[1]));
18919           continue;
18920         }
18921     
18922         // code
18923         if (cap = this.rules.code.exec(src)) {
18924           src = src.substring(cap[0].length);
18925           out += this.renderer.codespan(escape(cap[2], true));
18926           continue;
18927         }
18928     
18929         // br
18930         if (cap = this.rules.br.exec(src)) {
18931           src = src.substring(cap[0].length);
18932           out += this.renderer.br();
18933           continue;
18934         }
18935     
18936         // del (gfm)
18937         if (cap = this.rules.del.exec(src)) {
18938           src = src.substring(cap[0].length);
18939           out += this.renderer.del(this.output(cap[1]));
18940           continue;
18941         }
18942     
18943         // text
18944         if (cap = this.rules.text.exec(src)) {
18945           src = src.substring(cap[0].length);
18946           out += this.renderer.text(escape(this.smartypants(cap[0])));
18947           continue;
18948         }
18949     
18950         if (src) {
18951           throw new
18952             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18953         }
18954       }
18955     
18956       return out;
18957     };
18958     
18959     /**
18960      * Compile Link
18961      */
18962     
18963     InlineLexer.prototype.outputLink = function(cap, link) {
18964       var href = escape(link.href)
18965         , title = link.title ? escape(link.title) : null;
18966     
18967       return cap[0].charAt(0) !== '!'
18968         ? this.renderer.link(href, title, this.output(cap[1]))
18969         : this.renderer.image(href, title, escape(cap[1]));
18970     };
18971     
18972     /**
18973      * Smartypants Transformations
18974      */
18975     
18976     InlineLexer.prototype.smartypants = function(text) {
18977       if (!this.options.smartypants)  { return text; }
18978       return text
18979         // em-dashes
18980         .replace(/---/g, '\u2014')
18981         // en-dashes
18982         .replace(/--/g, '\u2013')
18983         // opening singles
18984         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18985         // closing singles & apostrophes
18986         .replace(/'/g, '\u2019')
18987         // opening doubles
18988         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18989         // closing doubles
18990         .replace(/"/g, '\u201d')
18991         // ellipses
18992         .replace(/\.{3}/g, '\u2026');
18993     };
18994     
18995     /**
18996      * Mangle Links
18997      */
18998     
18999     InlineLexer.prototype.mangle = function(text) {
19000       if (!this.options.mangle) { return text; }
19001       var out = ''
19002         , l = text.length
19003         , i = 0
19004         , ch;
19005     
19006       for (; i < l; i++) {
19007         ch = text.charCodeAt(i);
19008         if (Math.random() > 0.5) {
19009           ch = 'x' + ch.toString(16);
19010         }
19011         out += '&#' + ch + ';';
19012       }
19013     
19014       return out;
19015     };
19016     
19017     /**
19018      * Renderer
19019      */
19020     
19021      /**
19022          * eval:var:Renderer
19023     */
19024     
19025     var Renderer   = function (options) {
19026       this.options = options || {};
19027     }
19028     
19029     Renderer.prototype.code = function(code, lang, escaped) {
19030       if (this.options.highlight) {
19031         var out = this.options.highlight(code, lang);
19032         if (out != null && out !== code) {
19033           escaped = true;
19034           code = out;
19035         }
19036       } else {
19037             // hack!!! - it's already escapeD?
19038             escaped = true;
19039       }
19040     
19041       if (!lang) {
19042         return '<pre><code>'
19043           + (escaped ? code : escape(code, true))
19044           + '\n</code></pre>';
19045       }
19046     
19047       return '<pre><code class="'
19048         + this.options.langPrefix
19049         + escape(lang, true)
19050         + '">'
19051         + (escaped ? code : escape(code, true))
19052         + '\n</code></pre>\n';
19053     };
19054     
19055     Renderer.prototype.blockquote = function(quote) {
19056       return '<blockquote>\n' + quote + '</blockquote>\n';
19057     };
19058     
19059     Renderer.prototype.html = function(html) {
19060       return html;
19061     };
19062     
19063     Renderer.prototype.heading = function(text, level, raw) {
19064       return '<h'
19065         + level
19066         + ' id="'
19067         + this.options.headerPrefix
19068         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19069         + '">'
19070         + text
19071         + '</h'
19072         + level
19073         + '>\n';
19074     };
19075     
19076     Renderer.prototype.hr = function() {
19077       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19078     };
19079     
19080     Renderer.prototype.list = function(body, ordered) {
19081       var type = ordered ? 'ol' : 'ul';
19082       return '<' + type + '>\n' + body + '</' + type + '>\n';
19083     };
19084     
19085     Renderer.prototype.listitem = function(text) {
19086       return '<li>' + text + '</li>\n';
19087     };
19088     
19089     Renderer.prototype.paragraph = function(text) {
19090       return '<p>' + text + '</p>\n';
19091     };
19092     
19093     Renderer.prototype.table = function(header, body) {
19094       return '<table class="table table-striped">\n'
19095         + '<thead>\n'
19096         + header
19097         + '</thead>\n'
19098         + '<tbody>\n'
19099         + body
19100         + '</tbody>\n'
19101         + '</table>\n';
19102     };
19103     
19104     Renderer.prototype.tablerow = function(content) {
19105       return '<tr>\n' + content + '</tr>\n';
19106     };
19107     
19108     Renderer.prototype.tablecell = function(content, flags) {
19109       var type = flags.header ? 'th' : 'td';
19110       var tag = flags.align
19111         ? '<' + type + ' style="text-align:' + flags.align + '">'
19112         : '<' + type + '>';
19113       return tag + content + '</' + type + '>\n';
19114     };
19115     
19116     // span level renderer
19117     Renderer.prototype.strong = function(text) {
19118       return '<strong>' + text + '</strong>';
19119     };
19120     
19121     Renderer.prototype.em = function(text) {
19122       return '<em>' + text + '</em>';
19123     };
19124     
19125     Renderer.prototype.codespan = function(text) {
19126       return '<code>' + text + '</code>';
19127     };
19128     
19129     Renderer.prototype.br = function() {
19130       return this.options.xhtml ? '<br/>' : '<br>';
19131     };
19132     
19133     Renderer.prototype.del = function(text) {
19134       return '<del>' + text + '</del>';
19135     };
19136     
19137     Renderer.prototype.link = function(href, title, text) {
19138       if (this.options.sanitize) {
19139         try {
19140           var prot = decodeURIComponent(unescape(href))
19141             .replace(/[^\w:]/g, '')
19142             .toLowerCase();
19143         } catch (e) {
19144           return '';
19145         }
19146         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19147           return '';
19148         }
19149       }
19150       var out = '<a href="' + href + '"';
19151       if (title) {
19152         out += ' title="' + title + '"';
19153       }
19154       out += '>' + text + '</a>';
19155       return out;
19156     };
19157     
19158     Renderer.prototype.image = function(href, title, text) {
19159       var out = '<img src="' + href + '" alt="' + text + '"';
19160       if (title) {
19161         out += ' title="' + title + '"';
19162       }
19163       out += this.options.xhtml ? '/>' : '>';
19164       return out;
19165     };
19166     
19167     Renderer.prototype.text = function(text) {
19168       return text;
19169     };
19170     
19171     /**
19172      * Parsing & Compiling
19173      */
19174          /**
19175          * eval:var:Parser
19176     */
19177     
19178     var Parser= function (options) {
19179       this.tokens = [];
19180       this.token = null;
19181       this.options = options || marked.defaults;
19182       this.options.renderer = this.options.renderer || new Renderer;
19183       this.renderer = this.options.renderer;
19184       this.renderer.options = this.options;
19185     }
19186     
19187     /**
19188      * Static Parse Method
19189      */
19190     
19191     Parser.parse = function(src, options, renderer) {
19192       var parser = new Parser(options, renderer);
19193       return parser.parse(src);
19194     };
19195     
19196     /**
19197      * Parse Loop
19198      */
19199     
19200     Parser.prototype.parse = function(src) {
19201       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19202       this.tokens = src.reverse();
19203     
19204       var out = '';
19205       while (this.next()) {
19206         out += this.tok();
19207       }
19208     
19209       return out;
19210     };
19211     
19212     /**
19213      * Next Token
19214      */
19215     
19216     Parser.prototype.next = function() {
19217       return this.token = this.tokens.pop();
19218     };
19219     
19220     /**
19221      * Preview Next Token
19222      */
19223     
19224     Parser.prototype.peek = function() {
19225       return this.tokens[this.tokens.length - 1] || 0;
19226     };
19227     
19228     /**
19229      * Parse Text Tokens
19230      */
19231     
19232     Parser.prototype.parseText = function() {
19233       var body = this.token.text;
19234     
19235       while (this.peek().type === 'text') {
19236         body += '\n' + this.next().text;
19237       }
19238     
19239       return this.inline.output(body);
19240     };
19241     
19242     /**
19243      * Parse Current Token
19244      */
19245     
19246     Parser.prototype.tok = function() {
19247       switch (this.token.type) {
19248         case 'space': {
19249           return '';
19250         }
19251         case 'hr': {
19252           return this.renderer.hr();
19253         }
19254         case 'heading': {
19255           return this.renderer.heading(
19256             this.inline.output(this.token.text),
19257             this.token.depth,
19258             this.token.text);
19259         }
19260         case 'code': {
19261           return this.renderer.code(this.token.text,
19262             this.token.lang,
19263             this.token.escaped);
19264         }
19265         case 'table': {
19266           var header = ''
19267             , body = ''
19268             , i
19269             , row
19270             , cell
19271             , flags
19272             , j;
19273     
19274           // header
19275           cell = '';
19276           for (i = 0; i < this.token.header.length; i++) {
19277             flags = { header: true, align: this.token.align[i] };
19278             cell += this.renderer.tablecell(
19279               this.inline.output(this.token.header[i]),
19280               { header: true, align: this.token.align[i] }
19281             );
19282           }
19283           header += this.renderer.tablerow(cell);
19284     
19285           for (i = 0; i < this.token.cells.length; i++) {
19286             row = this.token.cells[i];
19287     
19288             cell = '';
19289             for (j = 0; j < row.length; j++) {
19290               cell += this.renderer.tablecell(
19291                 this.inline.output(row[j]),
19292                 { header: false, align: this.token.align[j] }
19293               );
19294             }
19295     
19296             body += this.renderer.tablerow(cell);
19297           }
19298           return this.renderer.table(header, body);
19299         }
19300         case 'blockquote_start': {
19301           var body = '';
19302     
19303           while (this.next().type !== 'blockquote_end') {
19304             body += this.tok();
19305           }
19306     
19307           return this.renderer.blockquote(body);
19308         }
19309         case 'list_start': {
19310           var body = ''
19311             , ordered = this.token.ordered;
19312     
19313           while (this.next().type !== 'list_end') {
19314             body += this.tok();
19315           }
19316     
19317           return this.renderer.list(body, ordered);
19318         }
19319         case 'list_item_start': {
19320           var body = '';
19321     
19322           while (this.next().type !== 'list_item_end') {
19323             body += this.token.type === 'text'
19324               ? this.parseText()
19325               : this.tok();
19326           }
19327     
19328           return this.renderer.listitem(body);
19329         }
19330         case 'loose_item_start': {
19331           var body = '';
19332     
19333           while (this.next().type !== 'list_item_end') {
19334             body += this.tok();
19335           }
19336     
19337           return this.renderer.listitem(body);
19338         }
19339         case 'html': {
19340           var html = !this.token.pre && !this.options.pedantic
19341             ? this.inline.output(this.token.text)
19342             : this.token.text;
19343           return this.renderer.html(html);
19344         }
19345         case 'paragraph': {
19346           return this.renderer.paragraph(this.inline.output(this.token.text));
19347         }
19348         case 'text': {
19349           return this.renderer.paragraph(this.parseText());
19350         }
19351       }
19352     };
19353   
19354     
19355     /**
19356      * Marked
19357      */
19358          /**
19359          * eval:var:marked
19360     */
19361     var marked = function (src, opt, callback) {
19362       if (callback || typeof opt === 'function') {
19363         if (!callback) {
19364           callback = opt;
19365           opt = null;
19366         }
19367     
19368         opt = merge({}, marked.defaults, opt || {});
19369     
19370         var highlight = opt.highlight
19371           , tokens
19372           , pending
19373           , i = 0;
19374     
19375         try {
19376           tokens = Lexer.lex(src, opt)
19377         } catch (e) {
19378           return callback(e);
19379         }
19380     
19381         pending = tokens.length;
19382          /**
19383          * eval:var:done
19384     */
19385         var done = function(err) {
19386           if (err) {
19387             opt.highlight = highlight;
19388             return callback(err);
19389           }
19390     
19391           var out;
19392     
19393           try {
19394             out = Parser.parse(tokens, opt);
19395           } catch (e) {
19396             err = e;
19397           }
19398     
19399           opt.highlight = highlight;
19400     
19401           return err
19402             ? callback(err)
19403             : callback(null, out);
19404         };
19405     
19406         if (!highlight || highlight.length < 3) {
19407           return done();
19408         }
19409     
19410         delete opt.highlight;
19411     
19412         if (!pending) { return done(); }
19413     
19414         for (; i < tokens.length; i++) {
19415           (function(token) {
19416             if (token.type !== 'code') {
19417               return --pending || done();
19418             }
19419             return highlight(token.text, token.lang, function(err, code) {
19420               if (err) { return done(err); }
19421               if (code == null || code === token.text) {
19422                 return --pending || done();
19423               }
19424               token.text = code;
19425               token.escaped = true;
19426               --pending || done();
19427             });
19428           })(tokens[i]);
19429         }
19430     
19431         return;
19432       }
19433       try {
19434         if (opt) { opt = merge({}, marked.defaults, opt); }
19435         return Parser.parse(Lexer.lex(src, opt), opt);
19436       } catch (e) {
19437         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19438         if ((opt || marked.defaults).silent) {
19439           return '<p>An error occured:</p><pre>'
19440             + escape(e.message + '', true)
19441             + '</pre>';
19442         }
19443         throw e;
19444       }
19445     }
19446     
19447     /**
19448      * Options
19449      */
19450     
19451     marked.options =
19452     marked.setOptions = function(opt) {
19453       merge(marked.defaults, opt);
19454       return marked;
19455     };
19456     
19457     marked.defaults = {
19458       gfm: true,
19459       tables: true,
19460       breaks: false,
19461       pedantic: false,
19462       sanitize: false,
19463       sanitizer: null,
19464       mangle: true,
19465       smartLists: false,
19466       silent: false,
19467       highlight: null,
19468       langPrefix: 'lang-',
19469       smartypants: false,
19470       headerPrefix: '',
19471       renderer: new Renderer,
19472       xhtml: false
19473     };
19474     
19475     /**
19476      * Expose
19477      */
19478     
19479     marked.Parser = Parser;
19480     marked.parser = Parser.parse;
19481     
19482     marked.Renderer = Renderer;
19483     
19484     marked.Lexer = Lexer;
19485     marked.lexer = Lexer.lex;
19486     
19487     marked.InlineLexer = InlineLexer;
19488     marked.inlineLexer = InlineLexer.output;
19489     
19490     marked.parse = marked;
19491     
19492     Roo.Markdown.marked = marked;
19493
19494 })();/*
19495  * Based on:
19496  * Ext JS Library 1.1.1
19497  * Copyright(c) 2006-2007, Ext JS, LLC.
19498  *
19499  * Originally Released Under LGPL - original licence link has changed is not relivant.
19500  *
19501  * Fork - LGPL
19502  * <script type="text/javascript">
19503  */
19504
19505
19506
19507 /*
19508  * These classes are derivatives of the similarly named classes in the YUI Library.
19509  * The original license:
19510  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19511  * Code licensed under the BSD License:
19512  * http://developer.yahoo.net/yui/license.txt
19513  */
19514
19515 (function() {
19516
19517 var Event=Roo.EventManager;
19518 var Dom=Roo.lib.Dom;
19519
19520 /**
19521  * @class Roo.dd.DragDrop
19522  * @extends Roo.util.Observable
19523  * Defines the interface and base operation of items that that can be
19524  * dragged or can be drop targets.  It was designed to be extended, overriding
19525  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19526  * Up to three html elements can be associated with a DragDrop instance:
19527  * <ul>
19528  * <li>linked element: the element that is passed into the constructor.
19529  * This is the element which defines the boundaries for interaction with
19530  * other DragDrop objects.</li>
19531  * <li>handle element(s): The drag operation only occurs if the element that
19532  * was clicked matches a handle element.  By default this is the linked
19533  * element, but there are times that you will want only a portion of the
19534  * linked element to initiate the drag operation, and the setHandleElId()
19535  * method provides a way to define this.</li>
19536  * <li>drag element: this represents the element that would be moved along
19537  * with the cursor during a drag operation.  By default, this is the linked
19538  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19539  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19540  * </li>
19541  * </ul>
19542  * This class should not be instantiated until the onload event to ensure that
19543  * the associated elements are available.
19544  * The following would define a DragDrop obj that would interact with any
19545  * other DragDrop obj in the "group1" group:
19546  * <pre>
19547  *  dd = new Roo.dd.DragDrop("div1", "group1");
19548  * </pre>
19549  * Since none of the event handlers have been implemented, nothing would
19550  * actually happen if you were to run the code above.  Normally you would
19551  * override this class or one of the default implementations, but you can
19552  * also override the methods you want on an instance of the class...
19553  * <pre>
19554  *  dd.onDragDrop = function(e, id) {
19555  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19556  *  }
19557  * </pre>
19558  * @constructor
19559  * @param {String} id of the element that is linked to this instance
19560  * @param {String} sGroup the group of related DragDrop objects
19561  * @param {object} config an object containing configurable attributes
19562  *                Valid properties for DragDrop:
19563  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19564  */
19565 Roo.dd.DragDrop = function(id, sGroup, config) {
19566     if (id) {
19567         this.init(id, sGroup, config);
19568     }
19569     
19570 };
19571
19572 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19573
19574     /**
19575      * The id of the element associated with this object.  This is what we
19576      * refer to as the "linked element" because the size and position of
19577      * this element is used to determine when the drag and drop objects have
19578      * interacted.
19579      * @property id
19580      * @type String
19581      */
19582     id: null,
19583
19584     /**
19585      * Configuration attributes passed into the constructor
19586      * @property config
19587      * @type object
19588      */
19589     config: null,
19590
19591     /**
19592      * The id of the element that will be dragged.  By default this is same
19593      * as the linked element , but could be changed to another element. Ex:
19594      * Roo.dd.DDProxy
19595      * @property dragElId
19596      * @type String
19597      * @private
19598      */
19599     dragElId: null,
19600
19601     /**
19602      * the id of the element that initiates the drag operation.  By default
19603      * this is the linked element, but could be changed to be a child of this
19604      * element.  This lets us do things like only starting the drag when the
19605      * header element within the linked html element is clicked.
19606      * @property handleElId
19607      * @type String
19608      * @private
19609      */
19610     handleElId: null,
19611
19612     /**
19613      * An associative array of HTML tags that will be ignored if clicked.
19614      * @property invalidHandleTypes
19615      * @type {string: string}
19616      */
19617     invalidHandleTypes: null,
19618
19619     /**
19620      * An associative array of ids for elements that will be ignored if clicked
19621      * @property invalidHandleIds
19622      * @type {string: string}
19623      */
19624     invalidHandleIds: null,
19625
19626     /**
19627      * An indexted array of css class names for elements that will be ignored
19628      * if clicked.
19629      * @property invalidHandleClasses
19630      * @type string[]
19631      */
19632     invalidHandleClasses: null,
19633
19634     /**
19635      * The linked element's absolute X position at the time the drag was
19636      * started
19637      * @property startPageX
19638      * @type int
19639      * @private
19640      */
19641     startPageX: 0,
19642
19643     /**
19644      * The linked element's absolute X position at the time the drag was
19645      * started
19646      * @property startPageY
19647      * @type int
19648      * @private
19649      */
19650     startPageY: 0,
19651
19652     /**
19653      * The group defines a logical collection of DragDrop objects that are
19654      * related.  Instances only get events when interacting with other
19655      * DragDrop object in the same group.  This lets us define multiple
19656      * groups using a single DragDrop subclass if we want.
19657      * @property groups
19658      * @type {string: string}
19659      */
19660     groups: null,
19661
19662     /**
19663      * Individual drag/drop instances can be locked.  This will prevent
19664      * onmousedown start drag.
19665      * @property locked
19666      * @type boolean
19667      * @private
19668      */
19669     locked: false,
19670
19671     /**
19672      * Lock this instance
19673      * @method lock
19674      */
19675     lock: function() { this.locked = true; },
19676
19677     /**
19678      * Unlock this instace
19679      * @method unlock
19680      */
19681     unlock: function() { this.locked = false; },
19682
19683     /**
19684      * By default, all insances can be a drop target.  This can be disabled by
19685      * setting isTarget to false.
19686      * @method isTarget
19687      * @type boolean
19688      */
19689     isTarget: true,
19690
19691     /**
19692      * The padding configured for this drag and drop object for calculating
19693      * the drop zone intersection with this object.
19694      * @method padding
19695      * @type int[]
19696      */
19697     padding: null,
19698
19699     /**
19700      * Cached reference to the linked element
19701      * @property _domRef
19702      * @private
19703      */
19704     _domRef: null,
19705
19706     /**
19707      * Internal typeof flag
19708      * @property __ygDragDrop
19709      * @private
19710      */
19711     __ygDragDrop: true,
19712
19713     /**
19714      * Set to true when horizontal contraints are applied
19715      * @property constrainX
19716      * @type boolean
19717      * @private
19718      */
19719     constrainX: false,
19720
19721     /**
19722      * Set to true when vertical contraints are applied
19723      * @property constrainY
19724      * @type boolean
19725      * @private
19726      */
19727     constrainY: false,
19728
19729     /**
19730      * The left constraint
19731      * @property minX
19732      * @type int
19733      * @private
19734      */
19735     minX: 0,
19736
19737     /**
19738      * The right constraint
19739      * @property maxX
19740      * @type int
19741      * @private
19742      */
19743     maxX: 0,
19744
19745     /**
19746      * The up constraint
19747      * @property minY
19748      * @type int
19749      * @type int
19750      * @private
19751      */
19752     minY: 0,
19753
19754     /**
19755      * The down constraint
19756      * @property maxY
19757      * @type int
19758      * @private
19759      */
19760     maxY: 0,
19761
19762     /**
19763      * Maintain offsets when we resetconstraints.  Set to true when you want
19764      * the position of the element relative to its parent to stay the same
19765      * when the page changes
19766      *
19767      * @property maintainOffset
19768      * @type boolean
19769      */
19770     maintainOffset: false,
19771
19772     /**
19773      * Array of pixel locations the element will snap to if we specified a
19774      * horizontal graduation/interval.  This array is generated automatically
19775      * when you define a tick interval.
19776      * @property xTicks
19777      * @type int[]
19778      */
19779     xTicks: null,
19780
19781     /**
19782      * Array of pixel locations the element will snap to if we specified a
19783      * vertical graduation/interval.  This array is generated automatically
19784      * when you define a tick interval.
19785      * @property yTicks
19786      * @type int[]
19787      */
19788     yTicks: null,
19789
19790     /**
19791      * By default the drag and drop instance will only respond to the primary
19792      * button click (left button for a right-handed mouse).  Set to true to
19793      * allow drag and drop to start with any mouse click that is propogated
19794      * by the browser
19795      * @property primaryButtonOnly
19796      * @type boolean
19797      */
19798     primaryButtonOnly: true,
19799
19800     /**
19801      * The availabe property is false until the linked dom element is accessible.
19802      * @property available
19803      * @type boolean
19804      */
19805     available: false,
19806
19807     /**
19808      * By default, drags can only be initiated if the mousedown occurs in the
19809      * region the linked element is.  This is done in part to work around a
19810      * bug in some browsers that mis-report the mousedown if the previous
19811      * mouseup happened outside of the window.  This property is set to true
19812      * if outer handles are defined.
19813      *
19814      * @property hasOuterHandles
19815      * @type boolean
19816      * @default false
19817      */
19818     hasOuterHandles: false,
19819
19820     /**
19821      * Code that executes immediately before the startDrag event
19822      * @method b4StartDrag
19823      * @private
19824      */
19825     b4StartDrag: function(x, y) { },
19826
19827     /**
19828      * Abstract method called after a drag/drop object is clicked
19829      * and the drag or mousedown time thresholds have beeen met.
19830      * @method startDrag
19831      * @param {int} X click location
19832      * @param {int} Y click location
19833      */
19834     startDrag: function(x, y) { /* override this */ },
19835
19836     /**
19837      * Code that executes immediately before the onDrag event
19838      * @method b4Drag
19839      * @private
19840      */
19841     b4Drag: function(e) { },
19842
19843     /**
19844      * Abstract method called during the onMouseMove event while dragging an
19845      * object.
19846      * @method onDrag
19847      * @param {Event} e the mousemove event
19848      */
19849     onDrag: function(e) { /* override this */ },
19850
19851     /**
19852      * Abstract method called when this element fist begins hovering over
19853      * another DragDrop obj
19854      * @method onDragEnter
19855      * @param {Event} e the mousemove event
19856      * @param {String|DragDrop[]} id In POINT mode, the element
19857      * id this is hovering over.  In INTERSECT mode, an array of one or more
19858      * dragdrop items being hovered over.
19859      */
19860     onDragEnter: function(e, id) { /* override this */ },
19861
19862     /**
19863      * Code that executes immediately before the onDragOver event
19864      * @method b4DragOver
19865      * @private
19866      */
19867     b4DragOver: function(e) { },
19868
19869     /**
19870      * Abstract method called when this element is hovering over another
19871      * DragDrop obj
19872      * @method onDragOver
19873      * @param {Event} e the mousemove event
19874      * @param {String|DragDrop[]} id In POINT mode, the element
19875      * id this is hovering over.  In INTERSECT mode, an array of dd items
19876      * being hovered over.
19877      */
19878     onDragOver: function(e, id) { /* override this */ },
19879
19880     /**
19881      * Code that executes immediately before the onDragOut event
19882      * @method b4DragOut
19883      * @private
19884      */
19885     b4DragOut: function(e) { },
19886
19887     /**
19888      * Abstract method called when we are no longer hovering over an element
19889      * @method onDragOut
19890      * @param {Event} e the mousemove event
19891      * @param {String|DragDrop[]} id In POINT mode, the element
19892      * id this was hovering over.  In INTERSECT mode, an array of dd items
19893      * that the mouse is no longer over.
19894      */
19895     onDragOut: function(e, id) { /* override this */ },
19896
19897     /**
19898      * Code that executes immediately before the onDragDrop event
19899      * @method b4DragDrop
19900      * @private
19901      */
19902     b4DragDrop: function(e) { },
19903
19904     /**
19905      * Abstract method called when this item is dropped on another DragDrop
19906      * obj
19907      * @method onDragDrop
19908      * @param {Event} e the mouseup event
19909      * @param {String|DragDrop[]} id In POINT mode, the element
19910      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19911      * was dropped on.
19912      */
19913     onDragDrop: function(e, id) { /* override this */ },
19914
19915     /**
19916      * Abstract method called when this item is dropped on an area with no
19917      * drop target
19918      * @method onInvalidDrop
19919      * @param {Event} e the mouseup event
19920      */
19921     onInvalidDrop: function(e) { /* override this */ },
19922
19923     /**
19924      * Code that executes immediately before the endDrag event
19925      * @method b4EndDrag
19926      * @private
19927      */
19928     b4EndDrag: function(e) { },
19929
19930     /**
19931      * Fired when we are done dragging the object
19932      * @method endDrag
19933      * @param {Event} e the mouseup event
19934      */
19935     endDrag: function(e) { /* override this */ },
19936
19937     /**
19938      * Code executed immediately before the onMouseDown event
19939      * @method b4MouseDown
19940      * @param {Event} e the mousedown event
19941      * @private
19942      */
19943     b4MouseDown: function(e) {  },
19944
19945     /**
19946      * Event handler that fires when a drag/drop obj gets a mousedown
19947      * @method onMouseDown
19948      * @param {Event} e the mousedown event
19949      */
19950     onMouseDown: function(e) { /* override this */ },
19951
19952     /**
19953      * Event handler that fires when a drag/drop obj gets a mouseup
19954      * @method onMouseUp
19955      * @param {Event} e the mouseup event
19956      */
19957     onMouseUp: function(e) { /* override this */ },
19958
19959     /**
19960      * Override the onAvailable method to do what is needed after the initial
19961      * position was determined.
19962      * @method onAvailable
19963      */
19964     onAvailable: function () {
19965     },
19966
19967     /*
19968      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19969      * @type Object
19970      */
19971     defaultPadding : {left:0, right:0, top:0, bottom:0},
19972
19973     /*
19974      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19975  *
19976  * Usage:
19977  <pre><code>
19978  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19979                 { dragElId: "existingProxyDiv" });
19980  dd.startDrag = function(){
19981      this.constrainTo("parent-id");
19982  };
19983  </code></pre>
19984  * Or you can initalize it using the {@link Roo.Element} object:
19985  <pre><code>
19986  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19987      startDrag : function(){
19988          this.constrainTo("parent-id");
19989      }
19990  });
19991  </code></pre>
19992      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19993      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19994      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19995      * an object containing the sides to pad. For example: {right:10, bottom:10}
19996      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19997      */
19998     constrainTo : function(constrainTo, pad, inContent){
19999         if(typeof pad == "number"){
20000             pad = {left: pad, right:pad, top:pad, bottom:pad};
20001         }
20002         pad = pad || this.defaultPadding;
20003         var b = Roo.get(this.getEl()).getBox();
20004         var ce = Roo.get(constrainTo);
20005         var s = ce.getScroll();
20006         var c, cd = ce.dom;
20007         if(cd == document.body){
20008             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20009         }else{
20010             xy = ce.getXY();
20011             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20012         }
20013
20014
20015         var topSpace = b.y - c.y;
20016         var leftSpace = b.x - c.x;
20017
20018         this.resetConstraints();
20019         this.setXConstraint(leftSpace - (pad.left||0), // left
20020                 c.width - leftSpace - b.width - (pad.right||0) //right
20021         );
20022         this.setYConstraint(topSpace - (pad.top||0), //top
20023                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20024         );
20025     },
20026
20027     /**
20028      * Returns a reference to the linked element
20029      * @method getEl
20030      * @return {HTMLElement} the html element
20031      */
20032     getEl: function() {
20033         if (!this._domRef) {
20034             this._domRef = Roo.getDom(this.id);
20035         }
20036
20037         return this._domRef;
20038     },
20039
20040     /**
20041      * Returns a reference to the actual element to drag.  By default this is
20042      * the same as the html element, but it can be assigned to another
20043      * element. An example of this can be found in Roo.dd.DDProxy
20044      * @method getDragEl
20045      * @return {HTMLElement} the html element
20046      */
20047     getDragEl: function() {
20048         return Roo.getDom(this.dragElId);
20049     },
20050
20051     /**
20052      * Sets up the DragDrop object.  Must be called in the constructor of any
20053      * Roo.dd.DragDrop subclass
20054      * @method init
20055      * @param id the id of the linked element
20056      * @param {String} sGroup the group of related items
20057      * @param {object} config configuration attributes
20058      */
20059     init: function(id, sGroup, config) {
20060         this.initTarget(id, sGroup, config);
20061         if (!Roo.isTouch) {
20062             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20063         }
20064         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20065         // Event.on(this.id, "selectstart", Event.preventDefault);
20066     },
20067
20068     /**
20069      * Initializes Targeting functionality only... the object does not
20070      * get a mousedown handler.
20071      * @method initTarget
20072      * @param id the id of the linked element
20073      * @param {String} sGroup the group of related items
20074      * @param {object} config configuration attributes
20075      */
20076     initTarget: function(id, sGroup, config) {
20077
20078         // configuration attributes
20079         this.config = config || {};
20080
20081         // create a local reference to the drag and drop manager
20082         this.DDM = Roo.dd.DDM;
20083         // initialize the groups array
20084         this.groups = {};
20085
20086         // assume that we have an element reference instead of an id if the
20087         // parameter is not a string
20088         if (typeof id !== "string") {
20089             id = Roo.id(id);
20090         }
20091
20092         // set the id
20093         this.id = id;
20094
20095         // add to an interaction group
20096         this.addToGroup((sGroup) ? sGroup : "default");
20097
20098         // We don't want to register this as the handle with the manager
20099         // so we just set the id rather than calling the setter.
20100         this.handleElId = id;
20101
20102         // the linked element is the element that gets dragged by default
20103         this.setDragElId(id);
20104
20105         // by default, clicked anchors will not start drag operations.
20106         this.invalidHandleTypes = { A: "A" };
20107         this.invalidHandleIds = {};
20108         this.invalidHandleClasses = [];
20109
20110         this.applyConfig();
20111
20112         this.handleOnAvailable();
20113     },
20114
20115     /**
20116      * Applies the configuration parameters that were passed into the constructor.
20117      * This is supposed to happen at each level through the inheritance chain.  So
20118      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20119      * DragDrop in order to get all of the parameters that are available in
20120      * each object.
20121      * @method applyConfig
20122      */
20123     applyConfig: function() {
20124
20125         // configurable properties:
20126         //    padding, isTarget, maintainOffset, primaryButtonOnly
20127         this.padding           = this.config.padding || [0, 0, 0, 0];
20128         this.isTarget          = (this.config.isTarget !== false);
20129         this.maintainOffset    = (this.config.maintainOffset);
20130         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20131
20132     },
20133
20134     /**
20135      * Executed when the linked element is available
20136      * @method handleOnAvailable
20137      * @private
20138      */
20139     handleOnAvailable: function() {
20140         this.available = true;
20141         this.resetConstraints();
20142         this.onAvailable();
20143     },
20144
20145      /**
20146      * Configures the padding for the target zone in px.  Effectively expands
20147      * (or reduces) the virtual object size for targeting calculations.
20148      * Supports css-style shorthand; if only one parameter is passed, all sides
20149      * will have that padding, and if only two are passed, the top and bottom
20150      * will have the first param, the left and right the second.
20151      * @method setPadding
20152      * @param {int} iTop    Top pad
20153      * @param {int} iRight  Right pad
20154      * @param {int} iBot    Bot pad
20155      * @param {int} iLeft   Left pad
20156      */
20157     setPadding: function(iTop, iRight, iBot, iLeft) {
20158         // this.padding = [iLeft, iRight, iTop, iBot];
20159         if (!iRight && 0 !== iRight) {
20160             this.padding = [iTop, iTop, iTop, iTop];
20161         } else if (!iBot && 0 !== iBot) {
20162             this.padding = [iTop, iRight, iTop, iRight];
20163         } else {
20164             this.padding = [iTop, iRight, iBot, iLeft];
20165         }
20166     },
20167
20168     /**
20169      * Stores the initial placement of the linked element.
20170      * @method setInitialPosition
20171      * @param {int} diffX   the X offset, default 0
20172      * @param {int} diffY   the Y offset, default 0
20173      */
20174     setInitPosition: function(diffX, diffY) {
20175         var el = this.getEl();
20176
20177         if (!this.DDM.verifyEl(el)) {
20178             return;
20179         }
20180
20181         var dx = diffX || 0;
20182         var dy = diffY || 0;
20183
20184         var p = Dom.getXY( el );
20185
20186         this.initPageX = p[0] - dx;
20187         this.initPageY = p[1] - dy;
20188
20189         this.lastPageX = p[0];
20190         this.lastPageY = p[1];
20191
20192
20193         this.setStartPosition(p);
20194     },
20195
20196     /**
20197      * Sets the start position of the element.  This is set when the obj
20198      * is initialized, the reset when a drag is started.
20199      * @method setStartPosition
20200      * @param pos current position (from previous lookup)
20201      * @private
20202      */
20203     setStartPosition: function(pos) {
20204         var p = pos || Dom.getXY( this.getEl() );
20205         this.deltaSetXY = null;
20206
20207         this.startPageX = p[0];
20208         this.startPageY = p[1];
20209     },
20210
20211     /**
20212      * Add this instance to a group of related drag/drop objects.  All
20213      * instances belong to at least one group, and can belong to as many
20214      * groups as needed.
20215      * @method addToGroup
20216      * @param sGroup {string} the name of the group
20217      */
20218     addToGroup: function(sGroup) {
20219         this.groups[sGroup] = true;
20220         this.DDM.regDragDrop(this, sGroup);
20221     },
20222
20223     /**
20224      * Remove's this instance from the supplied interaction group
20225      * @method removeFromGroup
20226      * @param {string}  sGroup  The group to drop
20227      */
20228     removeFromGroup: function(sGroup) {
20229         if (this.groups[sGroup]) {
20230             delete this.groups[sGroup];
20231         }
20232
20233         this.DDM.removeDDFromGroup(this, sGroup);
20234     },
20235
20236     /**
20237      * Allows you to specify that an element other than the linked element
20238      * will be moved with the cursor during a drag
20239      * @method setDragElId
20240      * @param id {string} the id of the element that will be used to initiate the drag
20241      */
20242     setDragElId: function(id) {
20243         this.dragElId = id;
20244     },
20245
20246     /**
20247      * Allows you to specify a child of the linked element that should be
20248      * used to initiate the drag operation.  An example of this would be if
20249      * you have a content div with text and links.  Clicking anywhere in the
20250      * content area would normally start the drag operation.  Use this method
20251      * to specify that an element inside of the content div is the element
20252      * that starts the drag operation.
20253      * @method setHandleElId
20254      * @param id {string} the id of the element that will be used to
20255      * initiate the drag.
20256      */
20257     setHandleElId: function(id) {
20258         if (typeof id !== "string") {
20259             id = Roo.id(id);
20260         }
20261         this.handleElId = id;
20262         this.DDM.regHandle(this.id, id);
20263     },
20264
20265     /**
20266      * Allows you to set an element outside of the linked element as a drag
20267      * handle
20268      * @method setOuterHandleElId
20269      * @param id the id of the element that will be used to initiate the drag
20270      */
20271     setOuterHandleElId: function(id) {
20272         if (typeof id !== "string") {
20273             id = Roo.id(id);
20274         }
20275         Event.on(id, "mousedown",
20276                 this.handleMouseDown, this);
20277         this.setHandleElId(id);
20278
20279         this.hasOuterHandles = true;
20280     },
20281
20282     /**
20283      * Remove all drag and drop hooks for this element
20284      * @method unreg
20285      */
20286     unreg: function() {
20287         Event.un(this.id, "mousedown",
20288                 this.handleMouseDown);
20289         Event.un(this.id, "touchstart",
20290                 this.handleMouseDown);
20291         this._domRef = null;
20292         this.DDM._remove(this);
20293     },
20294
20295     destroy : function(){
20296         this.unreg();
20297     },
20298
20299     /**
20300      * Returns true if this instance is locked, or the drag drop mgr is locked
20301      * (meaning that all drag/drop is disabled on the page.)
20302      * @method isLocked
20303      * @return {boolean} true if this obj or all drag/drop is locked, else
20304      * false
20305      */
20306     isLocked: function() {
20307         return (this.DDM.isLocked() || this.locked);
20308     },
20309
20310     /**
20311      * Fired when this object is clicked
20312      * @method handleMouseDown
20313      * @param {Event} e
20314      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20315      * @private
20316      */
20317     handleMouseDown: function(e, oDD){
20318      
20319         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20320             //Roo.log('not touch/ button !=0');
20321             return;
20322         }
20323         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20324             return; // double touch..
20325         }
20326         
20327
20328         if (this.isLocked()) {
20329             //Roo.log('locked');
20330             return;
20331         }
20332
20333         this.DDM.refreshCache(this.groups);
20334 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20335         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20336         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20337             //Roo.log('no outer handes or not over target');
20338                 // do nothing.
20339         } else {
20340 //            Roo.log('check validator');
20341             if (this.clickValidator(e)) {
20342 //                Roo.log('validate success');
20343                 // set the initial element position
20344                 this.setStartPosition();
20345
20346
20347                 this.b4MouseDown(e);
20348                 this.onMouseDown(e);
20349
20350                 this.DDM.handleMouseDown(e, this);
20351
20352                 this.DDM.stopEvent(e);
20353             } else {
20354
20355
20356             }
20357         }
20358     },
20359
20360     clickValidator: function(e) {
20361         var target = e.getTarget();
20362         return ( this.isValidHandleChild(target) &&
20363                     (this.id == this.handleElId ||
20364                         this.DDM.handleWasClicked(target, this.id)) );
20365     },
20366
20367     /**
20368      * Allows you to specify a tag name that should not start a drag operation
20369      * when clicked.  This is designed to facilitate embedding links within a
20370      * drag handle that do something other than start the drag.
20371      * @method addInvalidHandleType
20372      * @param {string} tagName the type of element to exclude
20373      */
20374     addInvalidHandleType: function(tagName) {
20375         var type = tagName.toUpperCase();
20376         this.invalidHandleTypes[type] = type;
20377     },
20378
20379     /**
20380      * Lets you to specify an element id for a child of a drag handle
20381      * that should not initiate a drag
20382      * @method addInvalidHandleId
20383      * @param {string} id the element id of the element you wish to ignore
20384      */
20385     addInvalidHandleId: function(id) {
20386         if (typeof id !== "string") {
20387             id = Roo.id(id);
20388         }
20389         this.invalidHandleIds[id] = id;
20390     },
20391
20392     /**
20393      * Lets you specify a css class of elements that will not initiate a drag
20394      * @method addInvalidHandleClass
20395      * @param {string} cssClass the class of the elements you wish to ignore
20396      */
20397     addInvalidHandleClass: function(cssClass) {
20398         this.invalidHandleClasses.push(cssClass);
20399     },
20400
20401     /**
20402      * Unsets an excluded tag name set by addInvalidHandleType
20403      * @method removeInvalidHandleType
20404      * @param {string} tagName the type of element to unexclude
20405      */
20406     removeInvalidHandleType: function(tagName) {
20407         var type = tagName.toUpperCase();
20408         // this.invalidHandleTypes[type] = null;
20409         delete this.invalidHandleTypes[type];
20410     },
20411
20412     /**
20413      * Unsets an invalid handle id
20414      * @method removeInvalidHandleId
20415      * @param {string} id the id of the element to re-enable
20416      */
20417     removeInvalidHandleId: function(id) {
20418         if (typeof id !== "string") {
20419             id = Roo.id(id);
20420         }
20421         delete this.invalidHandleIds[id];
20422     },
20423
20424     /**
20425      * Unsets an invalid css class
20426      * @method removeInvalidHandleClass
20427      * @param {string} cssClass the class of the element(s) you wish to
20428      * re-enable
20429      */
20430     removeInvalidHandleClass: function(cssClass) {
20431         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20432             if (this.invalidHandleClasses[i] == cssClass) {
20433                 delete this.invalidHandleClasses[i];
20434             }
20435         }
20436     },
20437
20438     /**
20439      * Checks the tag exclusion list to see if this click should be ignored
20440      * @method isValidHandleChild
20441      * @param {HTMLElement} node the HTMLElement to evaluate
20442      * @return {boolean} true if this is a valid tag type, false if not
20443      */
20444     isValidHandleChild: function(node) {
20445
20446         var valid = true;
20447         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20448         var nodeName;
20449         try {
20450             nodeName = node.nodeName.toUpperCase();
20451         } catch(e) {
20452             nodeName = node.nodeName;
20453         }
20454         valid = valid && !this.invalidHandleTypes[nodeName];
20455         valid = valid && !this.invalidHandleIds[node.id];
20456
20457         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20458             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20459         }
20460
20461
20462         return valid;
20463
20464     },
20465
20466     /**
20467      * Create the array of horizontal tick marks if an interval was specified
20468      * in setXConstraint().
20469      * @method setXTicks
20470      * @private
20471      */
20472     setXTicks: function(iStartX, iTickSize) {
20473         this.xTicks = [];
20474         this.xTickSize = iTickSize;
20475
20476         var tickMap = {};
20477
20478         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20479             if (!tickMap[i]) {
20480                 this.xTicks[this.xTicks.length] = i;
20481                 tickMap[i] = true;
20482             }
20483         }
20484
20485         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20486             if (!tickMap[i]) {
20487                 this.xTicks[this.xTicks.length] = i;
20488                 tickMap[i] = true;
20489             }
20490         }
20491
20492         this.xTicks.sort(this.DDM.numericSort) ;
20493     },
20494
20495     /**
20496      * Create the array of vertical tick marks if an interval was specified in
20497      * setYConstraint().
20498      * @method setYTicks
20499      * @private
20500      */
20501     setYTicks: function(iStartY, iTickSize) {
20502         this.yTicks = [];
20503         this.yTickSize = iTickSize;
20504
20505         var tickMap = {};
20506
20507         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20508             if (!tickMap[i]) {
20509                 this.yTicks[this.yTicks.length] = i;
20510                 tickMap[i] = true;
20511             }
20512         }
20513
20514         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20515             if (!tickMap[i]) {
20516                 this.yTicks[this.yTicks.length] = i;
20517                 tickMap[i] = true;
20518             }
20519         }
20520
20521         this.yTicks.sort(this.DDM.numericSort) ;
20522     },
20523
20524     /**
20525      * By default, the element can be dragged any place on the screen.  Use
20526      * this method to limit the horizontal travel of the element.  Pass in
20527      * 0,0 for the parameters if you want to lock the drag to the y axis.
20528      * @method setXConstraint
20529      * @param {int} iLeft the number of pixels the element can move to the left
20530      * @param {int} iRight the number of pixels the element can move to the
20531      * right
20532      * @param {int} iTickSize optional parameter for specifying that the
20533      * element
20534      * should move iTickSize pixels at a time.
20535      */
20536     setXConstraint: function(iLeft, iRight, iTickSize) {
20537         this.leftConstraint = iLeft;
20538         this.rightConstraint = iRight;
20539
20540         this.minX = this.initPageX - iLeft;
20541         this.maxX = this.initPageX + iRight;
20542         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20543
20544         this.constrainX = true;
20545     },
20546
20547     /**
20548      * Clears any constraints applied to this instance.  Also clears ticks
20549      * since they can't exist independent of a constraint at this time.
20550      * @method clearConstraints
20551      */
20552     clearConstraints: function() {
20553         this.constrainX = false;
20554         this.constrainY = false;
20555         this.clearTicks();
20556     },
20557
20558     /**
20559      * Clears any tick interval defined for this instance
20560      * @method clearTicks
20561      */
20562     clearTicks: function() {
20563         this.xTicks = null;
20564         this.yTicks = null;
20565         this.xTickSize = 0;
20566         this.yTickSize = 0;
20567     },
20568
20569     /**
20570      * By default, the element can be dragged any place on the screen.  Set
20571      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20572      * parameters if you want to lock the drag to the x axis.
20573      * @method setYConstraint
20574      * @param {int} iUp the number of pixels the element can move up
20575      * @param {int} iDown the number of pixels the element can move down
20576      * @param {int} iTickSize optional parameter for specifying that the
20577      * element should move iTickSize pixels at a time.
20578      */
20579     setYConstraint: function(iUp, iDown, iTickSize) {
20580         this.topConstraint = iUp;
20581         this.bottomConstraint = iDown;
20582
20583         this.minY = this.initPageY - iUp;
20584         this.maxY = this.initPageY + iDown;
20585         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20586
20587         this.constrainY = true;
20588
20589     },
20590
20591     /**
20592      * resetConstraints must be called if you manually reposition a dd element.
20593      * @method resetConstraints
20594      * @param {boolean} maintainOffset
20595      */
20596     resetConstraints: function() {
20597
20598
20599         // Maintain offsets if necessary
20600         if (this.initPageX || this.initPageX === 0) {
20601             // figure out how much this thing has moved
20602             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20603             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20604
20605             this.setInitPosition(dx, dy);
20606
20607         // This is the first time we have detected the element's position
20608         } else {
20609             this.setInitPosition();
20610         }
20611
20612         if (this.constrainX) {
20613             this.setXConstraint( this.leftConstraint,
20614                                  this.rightConstraint,
20615                                  this.xTickSize        );
20616         }
20617
20618         if (this.constrainY) {
20619             this.setYConstraint( this.topConstraint,
20620                                  this.bottomConstraint,
20621                                  this.yTickSize         );
20622         }
20623     },
20624
20625     /**
20626      * Normally the drag element is moved pixel by pixel, but we can specify
20627      * that it move a number of pixels at a time.  This method resolves the
20628      * location when we have it set up like this.
20629      * @method getTick
20630      * @param {int} val where we want to place the object
20631      * @param {int[]} tickArray sorted array of valid points
20632      * @return {int} the closest tick
20633      * @private
20634      */
20635     getTick: function(val, tickArray) {
20636
20637         if (!tickArray) {
20638             // If tick interval is not defined, it is effectively 1 pixel,
20639             // so we return the value passed to us.
20640             return val;
20641         } else if (tickArray[0] >= val) {
20642             // The value is lower than the first tick, so we return the first
20643             // tick.
20644             return tickArray[0];
20645         } else {
20646             for (var i=0, len=tickArray.length; i<len; ++i) {
20647                 var next = i + 1;
20648                 if (tickArray[next] && tickArray[next] >= val) {
20649                     var diff1 = val - tickArray[i];
20650                     var diff2 = tickArray[next] - val;
20651                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20652                 }
20653             }
20654
20655             // The value is larger than the last tick, so we return the last
20656             // tick.
20657             return tickArray[tickArray.length - 1];
20658         }
20659     },
20660
20661     /**
20662      * toString method
20663      * @method toString
20664      * @return {string} string representation of the dd obj
20665      */
20666     toString: function() {
20667         return ("DragDrop " + this.id);
20668     }
20669
20670 });
20671
20672 })();
20673 /*
20674  * Based on:
20675  * Ext JS Library 1.1.1
20676  * Copyright(c) 2006-2007, Ext JS, LLC.
20677  *
20678  * Originally Released Under LGPL - original licence link has changed is not relivant.
20679  *
20680  * Fork - LGPL
20681  * <script type="text/javascript">
20682  */
20683
20684
20685 /**
20686  * The drag and drop utility provides a framework for building drag and drop
20687  * applications.  In addition to enabling drag and drop for specific elements,
20688  * the drag and drop elements are tracked by the manager class, and the
20689  * interactions between the various elements are tracked during the drag and
20690  * the implementing code is notified about these important moments.
20691  */
20692
20693 // Only load the library once.  Rewriting the manager class would orphan
20694 // existing drag and drop instances.
20695 if (!Roo.dd.DragDropMgr) {
20696
20697 /**
20698  * @class Roo.dd.DragDropMgr
20699  * DragDropMgr is a singleton that tracks the element interaction for
20700  * all DragDrop items in the window.  Generally, you will not call
20701  * this class directly, but it does have helper methods that could
20702  * be useful in your DragDrop implementations.
20703  * @static
20704  */
20705 Roo.dd.DragDropMgr = function() {
20706
20707     var Event = Roo.EventManager;
20708
20709     return {
20710
20711         /**
20712          * Two dimensional Array of registered DragDrop objects.  The first
20713          * dimension is the DragDrop item group, the second the DragDrop
20714          * object.
20715          * @property ids
20716          * @type {string: string}
20717          * @private
20718          * @static
20719          */
20720         ids: {},
20721
20722         /**
20723          * Array of element ids defined as drag handles.  Used to determine
20724          * if the element that generated the mousedown event is actually the
20725          * handle and not the html element itself.
20726          * @property handleIds
20727          * @type {string: string}
20728          * @private
20729          * @static
20730          */
20731         handleIds: {},
20732
20733         /**
20734          * the DragDrop object that is currently being dragged
20735          * @property dragCurrent
20736          * @type DragDrop
20737          * @private
20738          * @static
20739          **/
20740         dragCurrent: null,
20741
20742         /**
20743          * the DragDrop object(s) that are being hovered over
20744          * @property dragOvers
20745          * @type Array
20746          * @private
20747          * @static
20748          */
20749         dragOvers: {},
20750
20751         /**
20752          * the X distance between the cursor and the object being dragged
20753          * @property deltaX
20754          * @type int
20755          * @private
20756          * @static
20757          */
20758         deltaX: 0,
20759
20760         /**
20761          * the Y distance between the cursor and the object being dragged
20762          * @property deltaY
20763          * @type int
20764          * @private
20765          * @static
20766          */
20767         deltaY: 0,
20768
20769         /**
20770          * Flag to determine if we should prevent the default behavior of the
20771          * events we define. By default this is true, but this can be set to
20772          * false if you need the default behavior (not recommended)
20773          * @property preventDefault
20774          * @type boolean
20775          * @static
20776          */
20777         preventDefault: true,
20778
20779         /**
20780          * Flag to determine if we should stop the propagation of the events
20781          * we generate. This is true by default but you may want to set it to
20782          * false if the html element contains other features that require the
20783          * mouse click.
20784          * @property stopPropagation
20785          * @type boolean
20786          * @static
20787          */
20788         stopPropagation: true,
20789
20790         /**
20791          * Internal flag that is set to true when drag and drop has been
20792          * intialized
20793          * @property initialized
20794          * @private
20795          * @static
20796          */
20797         initalized: false,
20798
20799         /**
20800          * All drag and drop can be disabled.
20801          * @property locked
20802          * @private
20803          * @static
20804          */
20805         locked: false,
20806
20807         /**
20808          * Called the first time an element is registered.
20809          * @method init
20810          * @private
20811          * @static
20812          */
20813         init: function() {
20814             this.initialized = true;
20815         },
20816
20817         /**
20818          * In point mode, drag and drop interaction is defined by the
20819          * location of the cursor during the drag/drop
20820          * @property POINT
20821          * @type int
20822          * @static
20823          */
20824         POINT: 0,
20825
20826         /**
20827          * In intersect mode, drag and drop interactio nis defined by the
20828          * overlap of two or more drag and drop objects.
20829          * @property INTERSECT
20830          * @type int
20831          * @static
20832          */
20833         INTERSECT: 1,
20834
20835         /**
20836          * The current drag and drop mode.  Default: POINT
20837          * @property mode
20838          * @type int
20839          * @static
20840          */
20841         mode: 0,
20842
20843         /**
20844          * Runs method on all drag and drop objects
20845          * @method _execOnAll
20846          * @private
20847          * @static
20848          */
20849         _execOnAll: function(sMethod, args) {
20850             for (var i in this.ids) {
20851                 for (var j in this.ids[i]) {
20852                     var oDD = this.ids[i][j];
20853                     if (! this.isTypeOfDD(oDD)) {
20854                         continue;
20855                     }
20856                     oDD[sMethod].apply(oDD, args);
20857                 }
20858             }
20859         },
20860
20861         /**
20862          * Drag and drop initialization.  Sets up the global event handlers
20863          * @method _onLoad
20864          * @private
20865          * @static
20866          */
20867         _onLoad: function() {
20868
20869             this.init();
20870
20871             if (!Roo.isTouch) {
20872                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20873                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20874             }
20875             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20876             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20877             
20878             Event.on(window,   "unload",    this._onUnload, this, true);
20879             Event.on(window,   "resize",    this._onResize, this, true);
20880             // Event.on(window,   "mouseout",    this._test);
20881
20882         },
20883
20884         /**
20885          * Reset constraints on all drag and drop objs
20886          * @method _onResize
20887          * @private
20888          * @static
20889          */
20890         _onResize: function(e) {
20891             this._execOnAll("resetConstraints", []);
20892         },
20893
20894         /**
20895          * Lock all drag and drop functionality
20896          * @method lock
20897          * @static
20898          */
20899         lock: function() { this.locked = true; },
20900
20901         /**
20902          * Unlock all drag and drop functionality
20903          * @method unlock
20904          * @static
20905          */
20906         unlock: function() { this.locked = false; },
20907
20908         /**
20909          * Is drag and drop locked?
20910          * @method isLocked
20911          * @return {boolean} True if drag and drop is locked, false otherwise.
20912          * @static
20913          */
20914         isLocked: function() { return this.locked; },
20915
20916         /**
20917          * Location cache that is set for all drag drop objects when a drag is
20918          * initiated, cleared when the drag is finished.
20919          * @property locationCache
20920          * @private
20921          * @static
20922          */
20923         locationCache: {},
20924
20925         /**
20926          * Set useCache to false if you want to force object the lookup of each
20927          * drag and drop linked element constantly during a drag.
20928          * @property useCache
20929          * @type boolean
20930          * @static
20931          */
20932         useCache: true,
20933
20934         /**
20935          * The number of pixels that the mouse needs to move after the
20936          * mousedown before the drag is initiated.  Default=3;
20937          * @property clickPixelThresh
20938          * @type int
20939          * @static
20940          */
20941         clickPixelThresh: 3,
20942
20943         /**
20944          * The number of milliseconds after the mousedown event to initiate the
20945          * drag if we don't get a mouseup event. Default=1000
20946          * @property clickTimeThresh
20947          * @type int
20948          * @static
20949          */
20950         clickTimeThresh: 350,
20951
20952         /**
20953          * Flag that indicates that either the drag pixel threshold or the
20954          * mousdown time threshold has been met
20955          * @property dragThreshMet
20956          * @type boolean
20957          * @private
20958          * @static
20959          */
20960         dragThreshMet: false,
20961
20962         /**
20963          * Timeout used for the click time threshold
20964          * @property clickTimeout
20965          * @type Object
20966          * @private
20967          * @static
20968          */
20969         clickTimeout: null,
20970
20971         /**
20972          * The X position of the mousedown event stored for later use when a
20973          * drag threshold is met.
20974          * @property startX
20975          * @type int
20976          * @private
20977          * @static
20978          */
20979         startX: 0,
20980
20981         /**
20982          * The Y position of the mousedown event stored for later use when a
20983          * drag threshold is met.
20984          * @property startY
20985          * @type int
20986          * @private
20987          * @static
20988          */
20989         startY: 0,
20990
20991         /**
20992          * Each DragDrop instance must be registered with the DragDropMgr.
20993          * This is executed in DragDrop.init()
20994          * @method regDragDrop
20995          * @param {DragDrop} oDD the DragDrop object to register
20996          * @param {String} sGroup the name of the group this element belongs to
20997          * @static
20998          */
20999         regDragDrop: function(oDD, sGroup) {
21000             if (!this.initialized) { this.init(); }
21001
21002             if (!this.ids[sGroup]) {
21003                 this.ids[sGroup] = {};
21004             }
21005             this.ids[sGroup][oDD.id] = oDD;
21006         },
21007
21008         /**
21009          * Removes the supplied dd instance from the supplied group. Executed
21010          * by DragDrop.removeFromGroup, so don't call this function directly.
21011          * @method removeDDFromGroup
21012          * @private
21013          * @static
21014          */
21015         removeDDFromGroup: function(oDD, sGroup) {
21016             if (!this.ids[sGroup]) {
21017                 this.ids[sGroup] = {};
21018             }
21019
21020             var obj = this.ids[sGroup];
21021             if (obj && obj[oDD.id]) {
21022                 delete obj[oDD.id];
21023             }
21024         },
21025
21026         /**
21027          * Unregisters a drag and drop item.  This is executed in
21028          * DragDrop.unreg, use that method instead of calling this directly.
21029          * @method _remove
21030          * @private
21031          * @static
21032          */
21033         _remove: function(oDD) {
21034             for (var g in oDD.groups) {
21035                 if (g && this.ids[g][oDD.id]) {
21036                     delete this.ids[g][oDD.id];
21037                 }
21038             }
21039             delete this.handleIds[oDD.id];
21040         },
21041
21042         /**
21043          * Each DragDrop handle element must be registered.  This is done
21044          * automatically when executing DragDrop.setHandleElId()
21045          * @method regHandle
21046          * @param {String} sDDId the DragDrop id this element is a handle for
21047          * @param {String} sHandleId the id of the element that is the drag
21048          * handle
21049          * @static
21050          */
21051         regHandle: function(sDDId, sHandleId) {
21052             if (!this.handleIds[sDDId]) {
21053                 this.handleIds[sDDId] = {};
21054             }
21055             this.handleIds[sDDId][sHandleId] = sHandleId;
21056         },
21057
21058         /**
21059          * Utility function to determine if a given element has been
21060          * registered as a drag drop item.
21061          * @method isDragDrop
21062          * @param {String} id the element id to check
21063          * @return {boolean} true if this element is a DragDrop item,
21064          * false otherwise
21065          * @static
21066          */
21067         isDragDrop: function(id) {
21068             return ( this.getDDById(id) ) ? true : false;
21069         },
21070
21071         /**
21072          * Returns the drag and drop instances that are in all groups the
21073          * passed in instance belongs to.
21074          * @method getRelated
21075          * @param {DragDrop} p_oDD the obj to get related data for
21076          * @param {boolean} bTargetsOnly if true, only return targetable objs
21077          * @return {DragDrop[]} the related instances
21078          * @static
21079          */
21080         getRelated: function(p_oDD, bTargetsOnly) {
21081             var oDDs = [];
21082             for (var i in p_oDD.groups) {
21083                 for (j in this.ids[i]) {
21084                     var dd = this.ids[i][j];
21085                     if (! this.isTypeOfDD(dd)) {
21086                         continue;
21087                     }
21088                     if (!bTargetsOnly || dd.isTarget) {
21089                         oDDs[oDDs.length] = dd;
21090                     }
21091                 }
21092             }
21093
21094             return oDDs;
21095         },
21096
21097         /**
21098          * Returns true if the specified dd target is a legal target for
21099          * the specifice drag obj
21100          * @method isLegalTarget
21101          * @param {DragDrop} the drag obj
21102          * @param {DragDrop} the target
21103          * @return {boolean} true if the target is a legal target for the
21104          * dd obj
21105          * @static
21106          */
21107         isLegalTarget: function (oDD, oTargetDD) {
21108             var targets = this.getRelated(oDD, true);
21109             for (var i=0, len=targets.length;i<len;++i) {
21110                 if (targets[i].id == oTargetDD.id) {
21111                     return true;
21112                 }
21113             }
21114
21115             return false;
21116         },
21117
21118         /**
21119          * My goal is to be able to transparently determine if an object is
21120          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21121          * returns "object", oDD.constructor.toString() always returns
21122          * "DragDrop" and not the name of the subclass.  So for now it just
21123          * evaluates a well-known variable in DragDrop.
21124          * @method isTypeOfDD
21125          * @param {Object} the object to evaluate
21126          * @return {boolean} true if typeof oDD = DragDrop
21127          * @static
21128          */
21129         isTypeOfDD: function (oDD) {
21130             return (oDD && oDD.__ygDragDrop);
21131         },
21132
21133         /**
21134          * Utility function to determine if a given element has been
21135          * registered as a drag drop handle for the given Drag Drop object.
21136          * @method isHandle
21137          * @param {String} id the element id to check
21138          * @return {boolean} true if this element is a DragDrop handle, false
21139          * otherwise
21140          * @static
21141          */
21142         isHandle: function(sDDId, sHandleId) {
21143             return ( this.handleIds[sDDId] &&
21144                             this.handleIds[sDDId][sHandleId] );
21145         },
21146
21147         /**
21148          * Returns the DragDrop instance for a given id
21149          * @method getDDById
21150          * @param {String} id the id of the DragDrop object
21151          * @return {DragDrop} the drag drop object, null if it is not found
21152          * @static
21153          */
21154         getDDById: function(id) {
21155             for (var i in this.ids) {
21156                 if (this.ids[i][id]) {
21157                     return this.ids[i][id];
21158                 }
21159             }
21160             return null;
21161         },
21162
21163         /**
21164          * Fired after a registered DragDrop object gets the mousedown event.
21165          * Sets up the events required to track the object being dragged
21166          * @method handleMouseDown
21167          * @param {Event} e the event
21168          * @param oDD the DragDrop object being dragged
21169          * @private
21170          * @static
21171          */
21172         handleMouseDown: function(e, oDD) {
21173             if(Roo.QuickTips){
21174                 Roo.QuickTips.disable();
21175             }
21176             this.currentTarget = e.getTarget();
21177
21178             this.dragCurrent = oDD;
21179
21180             var el = oDD.getEl();
21181
21182             // track start position
21183             this.startX = e.getPageX();
21184             this.startY = e.getPageY();
21185
21186             this.deltaX = this.startX - el.offsetLeft;
21187             this.deltaY = this.startY - el.offsetTop;
21188
21189             this.dragThreshMet = false;
21190
21191             this.clickTimeout = setTimeout(
21192                     function() {
21193                         var DDM = Roo.dd.DDM;
21194                         DDM.startDrag(DDM.startX, DDM.startY);
21195                     },
21196                     this.clickTimeThresh );
21197         },
21198
21199         /**
21200          * Fired when either the drag pixel threshol or the mousedown hold
21201          * time threshold has been met.
21202          * @method startDrag
21203          * @param x {int} the X position of the original mousedown
21204          * @param y {int} the Y position of the original mousedown
21205          * @static
21206          */
21207         startDrag: function(x, y) {
21208             clearTimeout(this.clickTimeout);
21209             if (this.dragCurrent) {
21210                 this.dragCurrent.b4StartDrag(x, y);
21211                 this.dragCurrent.startDrag(x, y);
21212             }
21213             this.dragThreshMet = true;
21214         },
21215
21216         /**
21217          * Internal function to handle the mouseup event.  Will be invoked
21218          * from the context of the document.
21219          * @method handleMouseUp
21220          * @param {Event} e the event
21221          * @private
21222          * @static
21223          */
21224         handleMouseUp: function(e) {
21225
21226             if(Roo.QuickTips){
21227                 Roo.QuickTips.enable();
21228             }
21229             if (! this.dragCurrent) {
21230                 return;
21231             }
21232
21233             clearTimeout(this.clickTimeout);
21234
21235             if (this.dragThreshMet) {
21236                 this.fireEvents(e, true);
21237             } else {
21238             }
21239
21240             this.stopDrag(e);
21241
21242             this.stopEvent(e);
21243         },
21244
21245         /**
21246          * Utility to stop event propagation and event default, if these
21247          * features are turned on.
21248          * @method stopEvent
21249          * @param {Event} e the event as returned by this.getEvent()
21250          * @static
21251          */
21252         stopEvent: function(e){
21253             if(this.stopPropagation) {
21254                 e.stopPropagation();
21255             }
21256
21257             if (this.preventDefault) {
21258                 e.preventDefault();
21259             }
21260         },
21261
21262         /**
21263          * Internal function to clean up event handlers after the drag
21264          * operation is complete
21265          * @method stopDrag
21266          * @param {Event} e the event
21267          * @private
21268          * @static
21269          */
21270         stopDrag: function(e) {
21271             // Fire the drag end event for the item that was dragged
21272             if (this.dragCurrent) {
21273                 if (this.dragThreshMet) {
21274                     this.dragCurrent.b4EndDrag(e);
21275                     this.dragCurrent.endDrag(e);
21276                 }
21277
21278                 this.dragCurrent.onMouseUp(e);
21279             }
21280
21281             this.dragCurrent = null;
21282             this.dragOvers = {};
21283         },
21284
21285         /**
21286          * Internal function to handle the mousemove event.  Will be invoked
21287          * from the context of the html element.
21288          *
21289          * @TODO figure out what we can do about mouse events lost when the
21290          * user drags objects beyond the window boundary.  Currently we can
21291          * detect this in internet explorer by verifying that the mouse is
21292          * down during the mousemove event.  Firefox doesn't give us the
21293          * button state on the mousemove event.
21294          * @method handleMouseMove
21295          * @param {Event} e the event
21296          * @private
21297          * @static
21298          */
21299         handleMouseMove: function(e) {
21300             if (! this.dragCurrent) {
21301                 return true;
21302             }
21303
21304             // var button = e.which || e.button;
21305
21306             // check for IE mouseup outside of page boundary
21307             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21308                 this.stopEvent(e);
21309                 return this.handleMouseUp(e);
21310             }
21311
21312             if (!this.dragThreshMet) {
21313                 var diffX = Math.abs(this.startX - e.getPageX());
21314                 var diffY = Math.abs(this.startY - e.getPageY());
21315                 if (diffX > this.clickPixelThresh ||
21316                             diffY > this.clickPixelThresh) {
21317                     this.startDrag(this.startX, this.startY);
21318                 }
21319             }
21320
21321             if (this.dragThreshMet) {
21322                 this.dragCurrent.b4Drag(e);
21323                 this.dragCurrent.onDrag(e);
21324                 if(!this.dragCurrent.moveOnly){
21325                     this.fireEvents(e, false);
21326                 }
21327             }
21328
21329             this.stopEvent(e);
21330
21331             return true;
21332         },
21333
21334         /**
21335          * Iterates over all of the DragDrop elements to find ones we are
21336          * hovering over or dropping on
21337          * @method fireEvents
21338          * @param {Event} e the event
21339          * @param {boolean} isDrop is this a drop op or a mouseover op?
21340          * @private
21341          * @static
21342          */
21343         fireEvents: function(e, isDrop) {
21344             var dc = this.dragCurrent;
21345
21346             // If the user did the mouse up outside of the window, we could
21347             // get here even though we have ended the drag.
21348             if (!dc || dc.isLocked()) {
21349                 return;
21350             }
21351
21352             var pt = e.getPoint();
21353
21354             // cache the previous dragOver array
21355             var oldOvers = [];
21356
21357             var outEvts   = [];
21358             var overEvts  = [];
21359             var dropEvts  = [];
21360             var enterEvts = [];
21361
21362             // Check to see if the object(s) we were hovering over is no longer
21363             // being hovered over so we can fire the onDragOut event
21364             for (var i in this.dragOvers) {
21365
21366                 var ddo = this.dragOvers[i];
21367
21368                 if (! this.isTypeOfDD(ddo)) {
21369                     continue;
21370                 }
21371
21372                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21373                     outEvts.push( ddo );
21374                 }
21375
21376                 oldOvers[i] = true;
21377                 delete this.dragOvers[i];
21378             }
21379
21380             for (var sGroup in dc.groups) {
21381
21382                 if ("string" != typeof sGroup) {
21383                     continue;
21384                 }
21385
21386                 for (i in this.ids[sGroup]) {
21387                     var oDD = this.ids[sGroup][i];
21388                     if (! this.isTypeOfDD(oDD)) {
21389                         continue;
21390                     }
21391
21392                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21393                         if (this.isOverTarget(pt, oDD, this.mode)) {
21394                             // look for drop interactions
21395                             if (isDrop) {
21396                                 dropEvts.push( oDD );
21397                             // look for drag enter and drag over interactions
21398                             } else {
21399
21400                                 // initial drag over: dragEnter fires
21401                                 if (!oldOvers[oDD.id]) {
21402                                     enterEvts.push( oDD );
21403                                 // subsequent drag overs: dragOver fires
21404                                 } else {
21405                                     overEvts.push( oDD );
21406                                 }
21407
21408                                 this.dragOvers[oDD.id] = oDD;
21409                             }
21410                         }
21411                     }
21412                 }
21413             }
21414
21415             if (this.mode) {
21416                 if (outEvts.length) {
21417                     dc.b4DragOut(e, outEvts);
21418                     dc.onDragOut(e, outEvts);
21419                 }
21420
21421                 if (enterEvts.length) {
21422                     dc.onDragEnter(e, enterEvts);
21423                 }
21424
21425                 if (overEvts.length) {
21426                     dc.b4DragOver(e, overEvts);
21427                     dc.onDragOver(e, overEvts);
21428                 }
21429
21430                 if (dropEvts.length) {
21431                     dc.b4DragDrop(e, dropEvts);
21432                     dc.onDragDrop(e, dropEvts);
21433                 }
21434
21435             } else {
21436                 // fire dragout events
21437                 var len = 0;
21438                 for (i=0, len=outEvts.length; i<len; ++i) {
21439                     dc.b4DragOut(e, outEvts[i].id);
21440                     dc.onDragOut(e, outEvts[i].id);
21441                 }
21442
21443                 // fire enter events
21444                 for (i=0,len=enterEvts.length; i<len; ++i) {
21445                     // dc.b4DragEnter(e, oDD.id);
21446                     dc.onDragEnter(e, enterEvts[i].id);
21447                 }
21448
21449                 // fire over events
21450                 for (i=0,len=overEvts.length; i<len; ++i) {
21451                     dc.b4DragOver(e, overEvts[i].id);
21452                     dc.onDragOver(e, overEvts[i].id);
21453                 }
21454
21455                 // fire drop events
21456                 for (i=0, len=dropEvts.length; i<len; ++i) {
21457                     dc.b4DragDrop(e, dropEvts[i].id);
21458                     dc.onDragDrop(e, dropEvts[i].id);
21459                 }
21460
21461             }
21462
21463             // notify about a drop that did not find a target
21464             if (isDrop && !dropEvts.length) {
21465                 dc.onInvalidDrop(e);
21466             }
21467
21468         },
21469
21470         /**
21471          * Helper function for getting the best match from the list of drag
21472          * and drop objects returned by the drag and drop events when we are
21473          * in INTERSECT mode.  It returns either the first object that the
21474          * cursor is over, or the object that has the greatest overlap with
21475          * the dragged element.
21476          * @method getBestMatch
21477          * @param  {DragDrop[]} dds The array of drag and drop objects
21478          * targeted
21479          * @return {DragDrop}       The best single match
21480          * @static
21481          */
21482         getBestMatch: function(dds) {
21483             var winner = null;
21484             // Return null if the input is not what we expect
21485             //if (!dds || !dds.length || dds.length == 0) {
21486                // winner = null;
21487             // If there is only one item, it wins
21488             //} else if (dds.length == 1) {
21489
21490             var len = dds.length;
21491
21492             if (len == 1) {
21493                 winner = dds[0];
21494             } else {
21495                 // Loop through the targeted items
21496                 for (var i=0; i<len; ++i) {
21497                     var dd = dds[i];
21498                     // If the cursor is over the object, it wins.  If the
21499                     // cursor is over multiple matches, the first one we come
21500                     // to wins.
21501                     if (dd.cursorIsOver) {
21502                         winner = dd;
21503                         break;
21504                     // Otherwise the object with the most overlap wins
21505                     } else {
21506                         if (!winner ||
21507                             winner.overlap.getArea() < dd.overlap.getArea()) {
21508                             winner = dd;
21509                         }
21510                     }
21511                 }
21512             }
21513
21514             return winner;
21515         },
21516
21517         /**
21518          * Refreshes the cache of the top-left and bottom-right points of the
21519          * drag and drop objects in the specified group(s).  This is in the
21520          * format that is stored in the drag and drop instance, so typical
21521          * usage is:
21522          * <code>
21523          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21524          * </code>
21525          * Alternatively:
21526          * <code>
21527          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21528          * </code>
21529          * @TODO this really should be an indexed array.  Alternatively this
21530          * method could accept both.
21531          * @method refreshCache
21532          * @param {Object} groups an associative array of groups to refresh
21533          * @static
21534          */
21535         refreshCache: function(groups) {
21536             for (var sGroup in groups) {
21537                 if ("string" != typeof sGroup) {
21538                     continue;
21539                 }
21540                 for (var i in this.ids[sGroup]) {
21541                     var oDD = this.ids[sGroup][i];
21542
21543                     if (this.isTypeOfDD(oDD)) {
21544                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21545                         var loc = this.getLocation(oDD);
21546                         if (loc) {
21547                             this.locationCache[oDD.id] = loc;
21548                         } else {
21549                             delete this.locationCache[oDD.id];
21550                             // this will unregister the drag and drop object if
21551                             // the element is not in a usable state
21552                             // oDD.unreg();
21553                         }
21554                     }
21555                 }
21556             }
21557         },
21558
21559         /**
21560          * This checks to make sure an element exists and is in the DOM.  The
21561          * main purpose is to handle cases where innerHTML is used to remove
21562          * drag and drop objects from the DOM.  IE provides an 'unspecified
21563          * error' when trying to access the offsetParent of such an element
21564          * @method verifyEl
21565          * @param {HTMLElement} el the element to check
21566          * @return {boolean} true if the element looks usable
21567          * @static
21568          */
21569         verifyEl: function(el) {
21570             if (el) {
21571                 var parent;
21572                 if(Roo.isIE){
21573                     try{
21574                         parent = el.offsetParent;
21575                     }catch(e){}
21576                 }else{
21577                     parent = el.offsetParent;
21578                 }
21579                 if (parent) {
21580                     return true;
21581                 }
21582             }
21583
21584             return false;
21585         },
21586
21587         /**
21588          * Returns a Region object containing the drag and drop element's position
21589          * and size, including the padding configured for it
21590          * @method getLocation
21591          * @param {DragDrop} oDD the drag and drop object to get the
21592          *                       location for
21593          * @return {Roo.lib.Region} a Region object representing the total area
21594          *                             the element occupies, including any padding
21595          *                             the instance is configured for.
21596          * @static
21597          */
21598         getLocation: function(oDD) {
21599             if (! this.isTypeOfDD(oDD)) {
21600                 return null;
21601             }
21602
21603             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21604
21605             try {
21606                 pos= Roo.lib.Dom.getXY(el);
21607             } catch (e) { }
21608
21609             if (!pos) {
21610                 return null;
21611             }
21612
21613             x1 = pos[0];
21614             x2 = x1 + el.offsetWidth;
21615             y1 = pos[1];
21616             y2 = y1 + el.offsetHeight;
21617
21618             t = y1 - oDD.padding[0];
21619             r = x2 + oDD.padding[1];
21620             b = y2 + oDD.padding[2];
21621             l = x1 - oDD.padding[3];
21622
21623             return new Roo.lib.Region( t, r, b, l );
21624         },
21625
21626         /**
21627          * Checks the cursor location to see if it over the target
21628          * @method isOverTarget
21629          * @param {Roo.lib.Point} pt The point to evaluate
21630          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21631          * @return {boolean} true if the mouse is over the target
21632          * @private
21633          * @static
21634          */
21635         isOverTarget: function(pt, oTarget, intersect) {
21636             // use cache if available
21637             var loc = this.locationCache[oTarget.id];
21638             if (!loc || !this.useCache) {
21639                 loc = this.getLocation(oTarget);
21640                 this.locationCache[oTarget.id] = loc;
21641
21642             }
21643
21644             if (!loc) {
21645                 return false;
21646             }
21647
21648             oTarget.cursorIsOver = loc.contains( pt );
21649
21650             // DragDrop is using this as a sanity check for the initial mousedown
21651             // in this case we are done.  In POINT mode, if the drag obj has no
21652             // contraints, we are also done. Otherwise we need to evaluate the
21653             // location of the target as related to the actual location of the
21654             // dragged element.
21655             var dc = this.dragCurrent;
21656             if (!dc || !dc.getTargetCoord ||
21657                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21658                 return oTarget.cursorIsOver;
21659             }
21660
21661             oTarget.overlap = null;
21662
21663             // Get the current location of the drag element, this is the
21664             // location of the mouse event less the delta that represents
21665             // where the original mousedown happened on the element.  We
21666             // need to consider constraints and ticks as well.
21667             var pos = dc.getTargetCoord(pt.x, pt.y);
21668
21669             var el = dc.getDragEl();
21670             var curRegion = new Roo.lib.Region( pos.y,
21671                                                    pos.x + el.offsetWidth,
21672                                                    pos.y + el.offsetHeight,
21673                                                    pos.x );
21674
21675             var overlap = curRegion.intersect(loc);
21676
21677             if (overlap) {
21678                 oTarget.overlap = overlap;
21679                 return (intersect) ? true : oTarget.cursorIsOver;
21680             } else {
21681                 return false;
21682             }
21683         },
21684
21685         /**
21686          * unload event handler
21687          * @method _onUnload
21688          * @private
21689          * @static
21690          */
21691         _onUnload: function(e, me) {
21692             Roo.dd.DragDropMgr.unregAll();
21693         },
21694
21695         /**
21696          * Cleans up the drag and drop events and objects.
21697          * @method unregAll
21698          * @private
21699          * @static
21700          */
21701         unregAll: function() {
21702
21703             if (this.dragCurrent) {
21704                 this.stopDrag();
21705                 this.dragCurrent = null;
21706             }
21707
21708             this._execOnAll("unreg", []);
21709
21710             for (i in this.elementCache) {
21711                 delete this.elementCache[i];
21712             }
21713
21714             this.elementCache = {};
21715             this.ids = {};
21716         },
21717
21718         /**
21719          * A cache of DOM elements
21720          * @property elementCache
21721          * @private
21722          * @static
21723          */
21724         elementCache: {},
21725
21726         /**
21727          * Get the wrapper for the DOM element specified
21728          * @method getElWrapper
21729          * @param {String} id the id of the element to get
21730          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21731          * @private
21732          * @deprecated This wrapper isn't that useful
21733          * @static
21734          */
21735         getElWrapper: function(id) {
21736             var oWrapper = this.elementCache[id];
21737             if (!oWrapper || !oWrapper.el) {
21738                 oWrapper = this.elementCache[id] =
21739                     new this.ElementWrapper(Roo.getDom(id));
21740             }
21741             return oWrapper;
21742         },
21743
21744         /**
21745          * Returns the actual DOM element
21746          * @method getElement
21747          * @param {String} id the id of the elment to get
21748          * @return {Object} The element
21749          * @deprecated use Roo.getDom instead
21750          * @static
21751          */
21752         getElement: function(id) {
21753             return Roo.getDom(id);
21754         },
21755
21756         /**
21757          * Returns the style property for the DOM element (i.e.,
21758          * document.getElById(id).style)
21759          * @method getCss
21760          * @param {String} id the id of the elment to get
21761          * @return {Object} The style property of the element
21762          * @deprecated use Roo.getDom instead
21763          * @static
21764          */
21765         getCss: function(id) {
21766             var el = Roo.getDom(id);
21767             return (el) ? el.style : null;
21768         },
21769
21770         /**
21771          * Inner class for cached elements
21772          * @class DragDropMgr.ElementWrapper
21773          * @for DragDropMgr
21774          * @private
21775          * @deprecated
21776          */
21777         ElementWrapper: function(el) {
21778                 /**
21779                  * The element
21780                  * @property el
21781                  */
21782                 this.el = el || null;
21783                 /**
21784                  * The element id
21785                  * @property id
21786                  */
21787                 this.id = this.el && el.id;
21788                 /**
21789                  * A reference to the style property
21790                  * @property css
21791                  */
21792                 this.css = this.el && el.style;
21793             },
21794
21795         /**
21796          * Returns the X position of an html element
21797          * @method getPosX
21798          * @param el the element for which to get the position
21799          * @return {int} the X coordinate
21800          * @for DragDropMgr
21801          * @deprecated use Roo.lib.Dom.getX instead
21802          * @static
21803          */
21804         getPosX: function(el) {
21805             return Roo.lib.Dom.getX(el);
21806         },
21807
21808         /**
21809          * Returns the Y position of an html element
21810          * @method getPosY
21811          * @param el the element for which to get the position
21812          * @return {int} the Y coordinate
21813          * @deprecated use Roo.lib.Dom.getY instead
21814          * @static
21815          */
21816         getPosY: function(el) {
21817             return Roo.lib.Dom.getY(el);
21818         },
21819
21820         /**
21821          * Swap two nodes.  In IE, we use the native method, for others we
21822          * emulate the IE behavior
21823          * @method swapNode
21824          * @param n1 the first node to swap
21825          * @param n2 the other node to swap
21826          * @static
21827          */
21828         swapNode: function(n1, n2) {
21829             if (n1.swapNode) {
21830                 n1.swapNode(n2);
21831             } else {
21832                 var p = n2.parentNode;
21833                 var s = n2.nextSibling;
21834
21835                 if (s == n1) {
21836                     p.insertBefore(n1, n2);
21837                 } else if (n2 == n1.nextSibling) {
21838                     p.insertBefore(n2, n1);
21839                 } else {
21840                     n1.parentNode.replaceChild(n2, n1);
21841                     p.insertBefore(n1, s);
21842                 }
21843             }
21844         },
21845
21846         /**
21847          * Returns the current scroll position
21848          * @method getScroll
21849          * @private
21850          * @static
21851          */
21852         getScroll: function () {
21853             var t, l, dde=document.documentElement, db=document.body;
21854             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21855                 t = dde.scrollTop;
21856                 l = dde.scrollLeft;
21857             } else if (db) {
21858                 t = db.scrollTop;
21859                 l = db.scrollLeft;
21860             } else {
21861
21862             }
21863             return { top: t, left: l };
21864         },
21865
21866         /**
21867          * Returns the specified element style property
21868          * @method getStyle
21869          * @param {HTMLElement} el          the element
21870          * @param {string}      styleProp   the style property
21871          * @return {string} The value of the style property
21872          * @deprecated use Roo.lib.Dom.getStyle
21873          * @static
21874          */
21875         getStyle: function(el, styleProp) {
21876             return Roo.fly(el).getStyle(styleProp);
21877         },
21878
21879         /**
21880          * Gets the scrollTop
21881          * @method getScrollTop
21882          * @return {int} the document's scrollTop
21883          * @static
21884          */
21885         getScrollTop: function () { return this.getScroll().top; },
21886
21887         /**
21888          * Gets the scrollLeft
21889          * @method getScrollLeft
21890          * @return {int} the document's scrollTop
21891          * @static
21892          */
21893         getScrollLeft: function () { return this.getScroll().left; },
21894
21895         /**
21896          * Sets the x/y position of an element to the location of the
21897          * target element.
21898          * @method moveToEl
21899          * @param {HTMLElement} moveEl      The element to move
21900          * @param {HTMLElement} targetEl    The position reference element
21901          * @static
21902          */
21903         moveToEl: function (moveEl, targetEl) {
21904             var aCoord = Roo.lib.Dom.getXY(targetEl);
21905             Roo.lib.Dom.setXY(moveEl, aCoord);
21906         },
21907
21908         /**
21909          * Numeric array sort function
21910          * @method numericSort
21911          * @static
21912          */
21913         numericSort: function(a, b) { return (a - b); },
21914
21915         /**
21916          * Internal counter
21917          * @property _timeoutCount
21918          * @private
21919          * @static
21920          */
21921         _timeoutCount: 0,
21922
21923         /**
21924          * Trying to make the load order less important.  Without this we get
21925          * an error if this file is loaded before the Event Utility.
21926          * @method _addListeners
21927          * @private
21928          * @static
21929          */
21930         _addListeners: function() {
21931             var DDM = Roo.dd.DDM;
21932             if ( Roo.lib.Event && document ) {
21933                 DDM._onLoad();
21934             } else {
21935                 if (DDM._timeoutCount > 2000) {
21936                 } else {
21937                     setTimeout(DDM._addListeners, 10);
21938                     if (document && document.body) {
21939                         DDM._timeoutCount += 1;
21940                     }
21941                 }
21942             }
21943         },
21944
21945         /**
21946          * Recursively searches the immediate parent and all child nodes for
21947          * the handle element in order to determine wheter or not it was
21948          * clicked.
21949          * @method handleWasClicked
21950          * @param node the html element to inspect
21951          * @static
21952          */
21953         handleWasClicked: function(node, id) {
21954             if (this.isHandle(id, node.id)) {
21955                 return true;
21956             } else {
21957                 // check to see if this is a text node child of the one we want
21958                 var p = node.parentNode;
21959
21960                 while (p) {
21961                     if (this.isHandle(id, p.id)) {
21962                         return true;
21963                     } else {
21964                         p = p.parentNode;
21965                     }
21966                 }
21967             }
21968
21969             return false;
21970         }
21971
21972     };
21973
21974 }();
21975
21976 // shorter alias, save a few bytes
21977 Roo.dd.DDM = Roo.dd.DragDropMgr;
21978 Roo.dd.DDM._addListeners();
21979
21980 }/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990
21991 /**
21992  * @class Roo.dd.DD
21993  * A DragDrop implementation where the linked element follows the
21994  * mouse cursor during a drag.
21995  * @extends Roo.dd.DragDrop
21996  * @constructor
21997  * @param {String} id the id of the linked element
21998  * @param {String} sGroup the group of related DragDrop items
21999  * @param {object} config an object containing configurable attributes
22000  *                Valid properties for DD:
22001  *                    scroll
22002  */
22003 Roo.dd.DD = function(id, sGroup, config) {
22004     if (id) {
22005         this.init(id, sGroup, config);
22006     }
22007 };
22008
22009 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22010
22011     /**
22012      * When set to true, the utility automatically tries to scroll the browser
22013      * window wehn a drag and drop element is dragged near the viewport boundary.
22014      * Defaults to true.
22015      * @property scroll
22016      * @type boolean
22017      */
22018     scroll: true,
22019
22020     /**
22021      * Sets the pointer offset to the distance between the linked element's top
22022      * left corner and the location the element was clicked
22023      * @method autoOffset
22024      * @param {int} iPageX the X coordinate of the click
22025      * @param {int} iPageY the Y coordinate of the click
22026      */
22027     autoOffset: function(iPageX, iPageY) {
22028         var x = iPageX - this.startPageX;
22029         var y = iPageY - this.startPageY;
22030         this.setDelta(x, y);
22031     },
22032
22033     /**
22034      * Sets the pointer offset.  You can call this directly to force the
22035      * offset to be in a particular location (e.g., pass in 0,0 to set it
22036      * to the center of the object)
22037      * @method setDelta
22038      * @param {int} iDeltaX the distance from the left
22039      * @param {int} iDeltaY the distance from the top
22040      */
22041     setDelta: function(iDeltaX, iDeltaY) {
22042         this.deltaX = iDeltaX;
22043         this.deltaY = iDeltaY;
22044     },
22045
22046     /**
22047      * Sets the drag element to the location of the mousedown or click event,
22048      * maintaining the cursor location relative to the location on the element
22049      * that was clicked.  Override this if you want to place the element in a
22050      * location other than where the cursor is.
22051      * @method setDragElPos
22052      * @param {int} iPageX the X coordinate of the mousedown or drag event
22053      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22054      */
22055     setDragElPos: function(iPageX, iPageY) {
22056         // the first time we do this, we are going to check to make sure
22057         // the element has css positioning
22058
22059         var el = this.getDragEl();
22060         this.alignElWithMouse(el, iPageX, iPageY);
22061     },
22062
22063     /**
22064      * Sets the element to the location of the mousedown or click event,
22065      * maintaining the cursor location relative to the location on the element
22066      * that was clicked.  Override this if you want to place the element in a
22067      * location other than where the cursor is.
22068      * @method alignElWithMouse
22069      * @param {HTMLElement} el the element to move
22070      * @param {int} iPageX the X coordinate of the mousedown or drag event
22071      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22072      */
22073     alignElWithMouse: function(el, iPageX, iPageY) {
22074         var oCoord = this.getTargetCoord(iPageX, iPageY);
22075         var fly = el.dom ? el : Roo.fly(el);
22076         if (!this.deltaSetXY) {
22077             var aCoord = [oCoord.x, oCoord.y];
22078             fly.setXY(aCoord);
22079             var newLeft = fly.getLeft(true);
22080             var newTop  = fly.getTop(true);
22081             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22082         } else {
22083             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22084         }
22085
22086         this.cachePosition(oCoord.x, oCoord.y);
22087         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22088         return oCoord;
22089     },
22090
22091     /**
22092      * Saves the most recent position so that we can reset the constraints and
22093      * tick marks on-demand.  We need to know this so that we can calculate the
22094      * number of pixels the element is offset from its original position.
22095      * @method cachePosition
22096      * @param iPageX the current x position (optional, this just makes it so we
22097      * don't have to look it up again)
22098      * @param iPageY the current y position (optional, this just makes it so we
22099      * don't have to look it up again)
22100      */
22101     cachePosition: function(iPageX, iPageY) {
22102         if (iPageX) {
22103             this.lastPageX = iPageX;
22104             this.lastPageY = iPageY;
22105         } else {
22106             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22107             this.lastPageX = aCoord[0];
22108             this.lastPageY = aCoord[1];
22109         }
22110     },
22111
22112     /**
22113      * Auto-scroll the window if the dragged object has been moved beyond the
22114      * visible window boundary.
22115      * @method autoScroll
22116      * @param {int} x the drag element's x position
22117      * @param {int} y the drag element's y position
22118      * @param {int} h the height of the drag element
22119      * @param {int} w the width of the drag element
22120      * @private
22121      */
22122     autoScroll: function(x, y, h, w) {
22123
22124         if (this.scroll) {
22125             // The client height
22126             var clientH = Roo.lib.Dom.getViewWidth();
22127
22128             // The client width
22129             var clientW = Roo.lib.Dom.getViewHeight();
22130
22131             // The amt scrolled down
22132             var st = this.DDM.getScrollTop();
22133
22134             // The amt scrolled right
22135             var sl = this.DDM.getScrollLeft();
22136
22137             // Location of the bottom of the element
22138             var bot = h + y;
22139
22140             // Location of the right of the element
22141             var right = w + x;
22142
22143             // The distance from the cursor to the bottom of the visible area,
22144             // adjusted so that we don't scroll if the cursor is beyond the
22145             // element drag constraints
22146             var toBot = (clientH + st - y - this.deltaY);
22147
22148             // The distance from the cursor to the right of the visible area
22149             var toRight = (clientW + sl - x - this.deltaX);
22150
22151
22152             // How close to the edge the cursor must be before we scroll
22153             // var thresh = (document.all) ? 100 : 40;
22154             var thresh = 40;
22155
22156             // How many pixels to scroll per autoscroll op.  This helps to reduce
22157             // clunky scrolling. IE is more sensitive about this ... it needs this
22158             // value to be higher.
22159             var scrAmt = (document.all) ? 80 : 30;
22160
22161             // Scroll down if we are near the bottom of the visible page and the
22162             // obj extends below the crease
22163             if ( bot > clientH && toBot < thresh ) {
22164                 window.scrollTo(sl, st + scrAmt);
22165             }
22166
22167             // Scroll up if the window is scrolled down and the top of the object
22168             // goes above the top border
22169             if ( y < st && st > 0 && y - st < thresh ) {
22170                 window.scrollTo(sl, st - scrAmt);
22171             }
22172
22173             // Scroll right if the obj is beyond the right border and the cursor is
22174             // near the border.
22175             if ( right > clientW && toRight < thresh ) {
22176                 window.scrollTo(sl + scrAmt, st);
22177             }
22178
22179             // Scroll left if the window has been scrolled to the right and the obj
22180             // extends past the left border
22181             if ( x < sl && sl > 0 && x - sl < thresh ) {
22182                 window.scrollTo(sl - scrAmt, st);
22183             }
22184         }
22185     },
22186
22187     /**
22188      * Finds the location the element should be placed if we want to move
22189      * it to where the mouse location less the click offset would place us.
22190      * @method getTargetCoord
22191      * @param {int} iPageX the X coordinate of the click
22192      * @param {int} iPageY the Y coordinate of the click
22193      * @return an object that contains the coordinates (Object.x and Object.y)
22194      * @private
22195      */
22196     getTargetCoord: function(iPageX, iPageY) {
22197
22198
22199         var x = iPageX - this.deltaX;
22200         var y = iPageY - this.deltaY;
22201
22202         if (this.constrainX) {
22203             if (x < this.minX) { x = this.minX; }
22204             if (x > this.maxX) { x = this.maxX; }
22205         }
22206
22207         if (this.constrainY) {
22208             if (y < this.minY) { y = this.minY; }
22209             if (y > this.maxY) { y = this.maxY; }
22210         }
22211
22212         x = this.getTick(x, this.xTicks);
22213         y = this.getTick(y, this.yTicks);
22214
22215
22216         return {x:x, y:y};
22217     },
22218
22219     /*
22220      * Sets up config options specific to this class. Overrides
22221      * Roo.dd.DragDrop, but all versions of this method through the
22222      * inheritance chain are called
22223      */
22224     applyConfig: function() {
22225         Roo.dd.DD.superclass.applyConfig.call(this);
22226         this.scroll = (this.config.scroll !== false);
22227     },
22228
22229     /*
22230      * Event that fires prior to the onMouseDown event.  Overrides
22231      * Roo.dd.DragDrop.
22232      */
22233     b4MouseDown: function(e) {
22234         // this.resetConstraints();
22235         this.autoOffset(e.getPageX(),
22236                             e.getPageY());
22237     },
22238
22239     /*
22240      * Event that fires prior to the onDrag event.  Overrides
22241      * Roo.dd.DragDrop.
22242      */
22243     b4Drag: function(e) {
22244         this.setDragElPos(e.getPageX(),
22245                             e.getPageY());
22246     },
22247
22248     toString: function() {
22249         return ("DD " + this.id);
22250     }
22251
22252     //////////////////////////////////////////////////////////////////////////
22253     // Debugging ygDragDrop events that can be overridden
22254     //////////////////////////////////////////////////////////////////////////
22255     /*
22256     startDrag: function(x, y) {
22257     },
22258
22259     onDrag: function(e) {
22260     },
22261
22262     onDragEnter: function(e, id) {
22263     },
22264
22265     onDragOver: function(e, id) {
22266     },
22267
22268     onDragOut: function(e, id) {
22269     },
22270
22271     onDragDrop: function(e, id) {
22272     },
22273
22274     endDrag: function(e) {
22275     }
22276
22277     */
22278
22279 });/*
22280  * Based on:
22281  * Ext JS Library 1.1.1
22282  * Copyright(c) 2006-2007, Ext JS, LLC.
22283  *
22284  * Originally Released Under LGPL - original licence link has changed is not relivant.
22285  *
22286  * Fork - LGPL
22287  * <script type="text/javascript">
22288  */
22289
22290 /**
22291  * @class Roo.dd.DDProxy
22292  * A DragDrop implementation that inserts an empty, bordered div into
22293  * the document that follows the cursor during drag operations.  At the time of
22294  * the click, the frame div is resized to the dimensions of the linked html
22295  * element, and moved to the exact location of the linked element.
22296  *
22297  * References to the "frame" element refer to the single proxy element that
22298  * was created to be dragged in place of all DDProxy elements on the
22299  * page.
22300  *
22301  * @extends Roo.dd.DD
22302  * @constructor
22303  * @param {String} id the id of the linked html element
22304  * @param {String} sGroup the group of related DragDrop objects
22305  * @param {object} config an object containing configurable attributes
22306  *                Valid properties for DDProxy in addition to those in DragDrop:
22307  *                   resizeFrame, centerFrame, dragElId
22308  */
22309 Roo.dd.DDProxy = function(id, sGroup, config) {
22310     if (id) {
22311         this.init(id, sGroup, config);
22312         this.initFrame();
22313     }
22314 };
22315
22316 /**
22317  * The default drag frame div id
22318  * @property Roo.dd.DDProxy.dragElId
22319  * @type String
22320  * @static
22321  */
22322 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22323
22324 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22325
22326     /**
22327      * By default we resize the drag frame to be the same size as the element
22328      * we want to drag (this is to get the frame effect).  We can turn it off
22329      * if we want a different behavior.
22330      * @property resizeFrame
22331      * @type boolean
22332      */
22333     resizeFrame: true,
22334
22335     /**
22336      * By default the frame is positioned exactly where the drag element is, so
22337      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22338      * you do not have constraints on the obj is to have the drag frame centered
22339      * around the cursor.  Set centerFrame to true for this effect.
22340      * @property centerFrame
22341      * @type boolean
22342      */
22343     centerFrame: false,
22344
22345     /**
22346      * Creates the proxy element if it does not yet exist
22347      * @method createFrame
22348      */
22349     createFrame: function() {
22350         var self = this;
22351         var body = document.body;
22352
22353         if (!body || !body.firstChild) {
22354             setTimeout( function() { self.createFrame(); }, 50 );
22355             return;
22356         }
22357
22358         var div = this.getDragEl();
22359
22360         if (!div) {
22361             div    = document.createElement("div");
22362             div.id = this.dragElId;
22363             var s  = div.style;
22364
22365             s.position   = "absolute";
22366             s.visibility = "hidden";
22367             s.cursor     = "move";
22368             s.border     = "2px solid #aaa";
22369             s.zIndex     = 999;
22370
22371             // appendChild can blow up IE if invoked prior to the window load event
22372             // while rendering a table.  It is possible there are other scenarios
22373             // that would cause this to happen as well.
22374             body.insertBefore(div, body.firstChild);
22375         }
22376     },
22377
22378     /**
22379      * Initialization for the drag frame element.  Must be called in the
22380      * constructor of all subclasses
22381      * @method initFrame
22382      */
22383     initFrame: function() {
22384         this.createFrame();
22385     },
22386
22387     applyConfig: function() {
22388         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22389
22390         this.resizeFrame = (this.config.resizeFrame !== false);
22391         this.centerFrame = (this.config.centerFrame);
22392         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22393     },
22394
22395     /**
22396      * Resizes the drag frame to the dimensions of the clicked object, positions
22397      * it over the object, and finally displays it
22398      * @method showFrame
22399      * @param {int} iPageX X click position
22400      * @param {int} iPageY Y click position
22401      * @private
22402      */
22403     showFrame: function(iPageX, iPageY) {
22404         var el = this.getEl();
22405         var dragEl = this.getDragEl();
22406         var s = dragEl.style;
22407
22408         this._resizeProxy();
22409
22410         if (this.centerFrame) {
22411             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22412                            Math.round(parseInt(s.height, 10)/2) );
22413         }
22414
22415         this.setDragElPos(iPageX, iPageY);
22416
22417         Roo.fly(dragEl).show();
22418     },
22419
22420     /**
22421      * The proxy is automatically resized to the dimensions of the linked
22422      * element when a drag is initiated, unless resizeFrame is set to false
22423      * @method _resizeProxy
22424      * @private
22425      */
22426     _resizeProxy: function() {
22427         if (this.resizeFrame) {
22428             var el = this.getEl();
22429             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22430         }
22431     },
22432
22433     // overrides Roo.dd.DragDrop
22434     b4MouseDown: function(e) {
22435         var x = e.getPageX();
22436         var y = e.getPageY();
22437         this.autoOffset(x, y);
22438         this.setDragElPos(x, y);
22439     },
22440
22441     // overrides Roo.dd.DragDrop
22442     b4StartDrag: function(x, y) {
22443         // show the drag frame
22444         this.showFrame(x, y);
22445     },
22446
22447     // overrides Roo.dd.DragDrop
22448     b4EndDrag: function(e) {
22449         Roo.fly(this.getDragEl()).hide();
22450     },
22451
22452     // overrides Roo.dd.DragDrop
22453     // By default we try to move the element to the last location of the frame.
22454     // This is so that the default behavior mirrors that of Roo.dd.DD.
22455     endDrag: function(e) {
22456
22457         var lel = this.getEl();
22458         var del = this.getDragEl();
22459
22460         // Show the drag frame briefly so we can get its position
22461         del.style.visibility = "";
22462
22463         this.beforeMove();
22464         // Hide the linked element before the move to get around a Safari
22465         // rendering bug.
22466         lel.style.visibility = "hidden";
22467         Roo.dd.DDM.moveToEl(lel, del);
22468         del.style.visibility = "hidden";
22469         lel.style.visibility = "";
22470
22471         this.afterDrag();
22472     },
22473
22474     beforeMove : function(){
22475
22476     },
22477
22478     afterDrag : function(){
22479
22480     },
22481
22482     toString: function() {
22483         return ("DDProxy " + this.id);
22484     }
22485
22486 });
22487 /*
22488  * Based on:
22489  * Ext JS Library 1.1.1
22490  * Copyright(c) 2006-2007, Ext JS, LLC.
22491  *
22492  * Originally Released Under LGPL - original licence link has changed is not relivant.
22493  *
22494  * Fork - LGPL
22495  * <script type="text/javascript">
22496  */
22497
22498  /**
22499  * @class Roo.dd.DDTarget
22500  * A DragDrop implementation that does not move, but can be a drop
22501  * target.  You would get the same result by simply omitting implementation
22502  * for the event callbacks, but this way we reduce the processing cost of the
22503  * event listener and the callbacks.
22504  * @extends Roo.dd.DragDrop
22505  * @constructor
22506  * @param {String} id the id of the element that is a drop target
22507  * @param {String} sGroup the group of related DragDrop objects
22508  * @param {object} config an object containing configurable attributes
22509  *                 Valid properties for DDTarget in addition to those in
22510  *                 DragDrop:
22511  *                    none
22512  */
22513 Roo.dd.DDTarget = function(id, sGroup, config) {
22514     if (id) {
22515         this.initTarget(id, sGroup, config);
22516     }
22517     if (config && (config.listeners || config.events)) { 
22518         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22519             listeners : config.listeners || {}, 
22520             events : config.events || {} 
22521         });    
22522     }
22523 };
22524
22525 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22526 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22527     toString: function() {
22528         return ("DDTarget " + this.id);
22529     }
22530 });
22531 /*
22532  * Based on:
22533  * Ext JS Library 1.1.1
22534  * Copyright(c) 2006-2007, Ext JS, LLC.
22535  *
22536  * Originally Released Under LGPL - original licence link has changed is not relivant.
22537  *
22538  * Fork - LGPL
22539  * <script type="text/javascript">
22540  */
22541  
22542
22543 /**
22544  * @class Roo.dd.ScrollManager
22545  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22546  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22547  * @static
22548  */
22549 Roo.dd.ScrollManager = function(){
22550     var ddm = Roo.dd.DragDropMgr;
22551     var els = {};
22552     var dragEl = null;
22553     var proc = {};
22554     
22555     
22556     
22557     var onStop = function(e){
22558         dragEl = null;
22559         clearProc();
22560     };
22561     
22562     var triggerRefresh = function(){
22563         if(ddm.dragCurrent){
22564              ddm.refreshCache(ddm.dragCurrent.groups);
22565         }
22566     };
22567     
22568     var doScroll = function(){
22569         if(ddm.dragCurrent){
22570             var dds = Roo.dd.ScrollManager;
22571             if(!dds.animate){
22572                 if(proc.el.scroll(proc.dir, dds.increment)){
22573                     triggerRefresh();
22574                 }
22575             }else{
22576                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22577             }
22578         }
22579     };
22580     
22581     var clearProc = function(){
22582         if(proc.id){
22583             clearInterval(proc.id);
22584         }
22585         proc.id = 0;
22586         proc.el = null;
22587         proc.dir = "";
22588     };
22589     
22590     var startProc = function(el, dir){
22591          Roo.log('scroll startproc');
22592         clearProc();
22593         proc.el = el;
22594         proc.dir = dir;
22595         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22596     };
22597     
22598     var onFire = function(e, isDrop){
22599        
22600         if(isDrop || !ddm.dragCurrent){ return; }
22601         var dds = Roo.dd.ScrollManager;
22602         if(!dragEl || dragEl != ddm.dragCurrent){
22603             dragEl = ddm.dragCurrent;
22604             // refresh regions on drag start
22605             dds.refreshCache();
22606         }
22607         
22608         var xy = Roo.lib.Event.getXY(e);
22609         var pt = new Roo.lib.Point(xy[0], xy[1]);
22610         for(var id in els){
22611             var el = els[id], r = el._region;
22612             if(r && r.contains(pt) && el.isScrollable()){
22613                 if(r.bottom - pt.y <= dds.thresh){
22614                     if(proc.el != el){
22615                         startProc(el, "down");
22616                     }
22617                     return;
22618                 }else if(r.right - pt.x <= dds.thresh){
22619                     if(proc.el != el){
22620                         startProc(el, "left");
22621                     }
22622                     return;
22623                 }else if(pt.y - r.top <= dds.thresh){
22624                     if(proc.el != el){
22625                         startProc(el, "up");
22626                     }
22627                     return;
22628                 }else if(pt.x - r.left <= dds.thresh){
22629                     if(proc.el != el){
22630                         startProc(el, "right");
22631                     }
22632                     return;
22633                 }
22634             }
22635         }
22636         clearProc();
22637     };
22638     
22639     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22640     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22641     
22642     return {
22643         /**
22644          * Registers new overflow element(s) to auto scroll
22645          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22646          */
22647         register : function(el){
22648             if(el instanceof Array){
22649                 for(var i = 0, len = el.length; i < len; i++) {
22650                         this.register(el[i]);
22651                 }
22652             }else{
22653                 el = Roo.get(el);
22654                 els[el.id] = el;
22655             }
22656             Roo.dd.ScrollManager.els = els;
22657         },
22658         
22659         /**
22660          * Unregisters overflow element(s) so they are no longer scrolled
22661          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22662          */
22663         unregister : function(el){
22664             if(el instanceof Array){
22665                 for(var i = 0, len = el.length; i < len; i++) {
22666                         this.unregister(el[i]);
22667                 }
22668             }else{
22669                 el = Roo.get(el);
22670                 delete els[el.id];
22671             }
22672         },
22673         
22674         /**
22675          * The number of pixels from the edge of a container the pointer needs to be to 
22676          * trigger scrolling (defaults to 25)
22677          * @type Number
22678          */
22679         thresh : 25,
22680         
22681         /**
22682          * The number of pixels to scroll in each scroll increment (defaults to 50)
22683          * @type Number
22684          */
22685         increment : 100,
22686         
22687         /**
22688          * The frequency of scrolls in milliseconds (defaults to 500)
22689          * @type Number
22690          */
22691         frequency : 500,
22692         
22693         /**
22694          * True to animate the scroll (defaults to true)
22695          * @type Boolean
22696          */
22697         animate: true,
22698         
22699         /**
22700          * The animation duration in seconds - 
22701          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22702          * @type Number
22703          */
22704         animDuration: .4,
22705         
22706         /**
22707          * Manually trigger a cache refresh.
22708          */
22709         refreshCache : function(){
22710             for(var id in els){
22711                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22712                     els[id]._region = els[id].getRegion();
22713                 }
22714             }
22715         }
22716     };
22717 }();/*
22718  * Based on:
22719  * Ext JS Library 1.1.1
22720  * Copyright(c) 2006-2007, Ext JS, LLC.
22721  *
22722  * Originally Released Under LGPL - original licence link has changed is not relivant.
22723  *
22724  * Fork - LGPL
22725  * <script type="text/javascript">
22726  */
22727  
22728
22729 /**
22730  * @class Roo.dd.Registry
22731  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22732  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22733  * @static
22734  */
22735 Roo.dd.Registry = function(){
22736     var elements = {}; 
22737     var handles = {}; 
22738     var autoIdSeed = 0;
22739
22740     var getId = function(el, autogen){
22741         if(typeof el == "string"){
22742             return el;
22743         }
22744         var id = el.id;
22745         if(!id && autogen !== false){
22746             id = "roodd-" + (++autoIdSeed);
22747             el.id = id;
22748         }
22749         return id;
22750     };
22751     
22752     return {
22753     /**
22754      * Register a drag drop element
22755      * @param {String|HTMLElement} element The id or DOM node to register
22756      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22757      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22758      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22759      * populated in the data object (if applicable):
22760      * <pre>
22761 Value      Description<br />
22762 ---------  ------------------------------------------<br />
22763 handles    Array of DOM nodes that trigger dragging<br />
22764            for the element being registered<br />
22765 isHandle   True if the element passed in triggers<br />
22766            dragging itself, else false
22767 </pre>
22768      */
22769         register : function(el, data){
22770             data = data || {};
22771             if(typeof el == "string"){
22772                 el = document.getElementById(el);
22773             }
22774             data.ddel = el;
22775             elements[getId(el)] = data;
22776             if(data.isHandle !== false){
22777                 handles[data.ddel.id] = data;
22778             }
22779             if(data.handles){
22780                 var hs = data.handles;
22781                 for(var i = 0, len = hs.length; i < len; i++){
22782                         handles[getId(hs[i])] = data;
22783                 }
22784             }
22785         },
22786
22787     /**
22788      * Unregister a drag drop element
22789      * @param {String|HTMLElement}  element The id or DOM node to unregister
22790      */
22791         unregister : function(el){
22792             var id = getId(el, false);
22793             var data = elements[id];
22794             if(data){
22795                 delete elements[id];
22796                 if(data.handles){
22797                     var hs = data.handles;
22798                     for(var i = 0, len = hs.length; i < len; i++){
22799                         delete handles[getId(hs[i], false)];
22800                     }
22801                 }
22802             }
22803         },
22804
22805     /**
22806      * Returns the handle registered for a DOM Node by id
22807      * @param {String|HTMLElement} id The DOM node or id to look up
22808      * @return {Object} handle The custom handle data
22809      */
22810         getHandle : function(id){
22811             if(typeof id != "string"){ // must be element?
22812                 id = id.id;
22813             }
22814             return handles[id];
22815         },
22816
22817     /**
22818      * Returns the handle that is registered for the DOM node that is the target of the event
22819      * @param {Event} e The event
22820      * @return {Object} handle The custom handle data
22821      */
22822         getHandleFromEvent : function(e){
22823             var t = Roo.lib.Event.getTarget(e);
22824             return t ? handles[t.id] : null;
22825         },
22826
22827     /**
22828      * Returns a custom data object that is registered for a DOM node by id
22829      * @param {String|HTMLElement} id The DOM node or id to look up
22830      * @return {Object} data The custom data
22831      */
22832         getTarget : function(id){
22833             if(typeof id != "string"){ // must be element?
22834                 id = id.id;
22835             }
22836             return elements[id];
22837         },
22838
22839     /**
22840      * Returns a custom data object that is registered for the DOM node that is the target of the event
22841      * @param {Event} e The event
22842      * @return {Object} data The custom data
22843      */
22844         getTargetFromEvent : function(e){
22845             var t = Roo.lib.Event.getTarget(e);
22846             return t ? elements[t.id] || handles[t.id] : null;
22847         }
22848     };
22849 }();/*
22850  * Based on:
22851  * Ext JS Library 1.1.1
22852  * Copyright(c) 2006-2007, Ext JS, LLC.
22853  *
22854  * Originally Released Under LGPL - original licence link has changed is not relivant.
22855  *
22856  * Fork - LGPL
22857  * <script type="text/javascript">
22858  */
22859  
22860
22861 /**
22862  * @class Roo.dd.StatusProxy
22863  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22864  * default drag proxy used by all Roo.dd components.
22865  * @constructor
22866  * @param {Object} config
22867  */
22868 Roo.dd.StatusProxy = function(config){
22869     Roo.apply(this, config);
22870     this.id = this.id || Roo.id();
22871     this.el = new Roo.Layer({
22872         dh: {
22873             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22874                 {tag: "div", cls: "x-dd-drop-icon"},
22875                 {tag: "div", cls: "x-dd-drag-ghost"}
22876             ]
22877         }, 
22878         shadow: !config || config.shadow !== false
22879     });
22880     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22881     this.dropStatus = this.dropNotAllowed;
22882 };
22883
22884 Roo.dd.StatusProxy.prototype = {
22885     /**
22886      * @cfg {String} dropAllowed
22887      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22888      */
22889     dropAllowed : "x-dd-drop-ok",
22890     /**
22891      * @cfg {String} dropNotAllowed
22892      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22893      */
22894     dropNotAllowed : "x-dd-drop-nodrop",
22895
22896     /**
22897      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22898      * over the current target element.
22899      * @param {String} cssClass The css class for the new drop status indicator image
22900      */
22901     setStatus : function(cssClass){
22902         cssClass = cssClass || this.dropNotAllowed;
22903         if(this.dropStatus != cssClass){
22904             this.el.replaceClass(this.dropStatus, cssClass);
22905             this.dropStatus = cssClass;
22906         }
22907     },
22908
22909     /**
22910      * Resets the status indicator to the default dropNotAllowed value
22911      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22912      */
22913     reset : function(clearGhost){
22914         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22915         this.dropStatus = this.dropNotAllowed;
22916         if(clearGhost){
22917             this.ghost.update("");
22918         }
22919     },
22920
22921     /**
22922      * Updates the contents of the ghost element
22923      * @param {String} html The html that will replace the current innerHTML of the ghost element
22924      */
22925     update : function(html){
22926         if(typeof html == "string"){
22927             this.ghost.update(html);
22928         }else{
22929             this.ghost.update("");
22930             html.style.margin = "0";
22931             this.ghost.dom.appendChild(html);
22932         }
22933         // ensure float = none set?? cant remember why though.
22934         var el = this.ghost.dom.firstChild;
22935                 if(el){
22936                         Roo.fly(el).setStyle('float', 'none');
22937                 }
22938     },
22939     
22940     /**
22941      * Returns the underlying proxy {@link Roo.Layer}
22942      * @return {Roo.Layer} el
22943     */
22944     getEl : function(){
22945         return this.el;
22946     },
22947
22948     /**
22949      * Returns the ghost element
22950      * @return {Roo.Element} el
22951      */
22952     getGhost : function(){
22953         return this.ghost;
22954     },
22955
22956     /**
22957      * Hides the proxy
22958      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22959      */
22960     hide : function(clear){
22961         this.el.hide();
22962         if(clear){
22963             this.reset(true);
22964         }
22965     },
22966
22967     /**
22968      * Stops the repair animation if it's currently running
22969      */
22970     stop : function(){
22971         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22972             this.anim.stop();
22973         }
22974     },
22975
22976     /**
22977      * Displays this proxy
22978      */
22979     show : function(){
22980         this.el.show();
22981     },
22982
22983     /**
22984      * Force the Layer to sync its shadow and shim positions to the element
22985      */
22986     sync : function(){
22987         this.el.sync();
22988     },
22989
22990     /**
22991      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22992      * invalid drop operation by the item being dragged.
22993      * @param {Array} xy The XY position of the element ([x, y])
22994      * @param {Function} callback The function to call after the repair is complete
22995      * @param {Object} scope The scope in which to execute the callback
22996      */
22997     repair : function(xy, callback, scope){
22998         this.callback = callback;
22999         this.scope = scope;
23000         if(xy && this.animRepair !== false){
23001             this.el.addClass("x-dd-drag-repair");
23002             this.el.hideUnders(true);
23003             this.anim = this.el.shift({
23004                 duration: this.repairDuration || .5,
23005                 easing: 'easeOut',
23006                 xy: xy,
23007                 stopFx: true,
23008                 callback: this.afterRepair,
23009                 scope: this
23010             });
23011         }else{
23012             this.afterRepair();
23013         }
23014     },
23015
23016     // private
23017     afterRepair : function(){
23018         this.hide(true);
23019         if(typeof this.callback == "function"){
23020             this.callback.call(this.scope || this);
23021         }
23022         this.callback = null;
23023         this.scope = null;
23024     }
23025 };/*
23026  * Based on:
23027  * Ext JS Library 1.1.1
23028  * Copyright(c) 2006-2007, Ext JS, LLC.
23029  *
23030  * Originally Released Under LGPL - original licence link has changed is not relivant.
23031  *
23032  * Fork - LGPL
23033  * <script type="text/javascript">
23034  */
23035
23036 /**
23037  * @class Roo.dd.DragSource
23038  * @extends Roo.dd.DDProxy
23039  * A simple class that provides the basic implementation needed to make any element draggable.
23040  * @constructor
23041  * @param {String/HTMLElement/Element} el The container element
23042  * @param {Object} config
23043  */
23044 Roo.dd.DragSource = function(el, config){
23045     this.el = Roo.get(el);
23046     this.dragData = {};
23047     
23048     Roo.apply(this, config);
23049     
23050     if(!this.proxy){
23051         this.proxy = new Roo.dd.StatusProxy();
23052     }
23053
23054     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23055           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23056     
23057     this.dragging = false;
23058 };
23059
23060 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23061     /**
23062      * @cfg {String} dropAllowed
23063      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23064      */
23065     dropAllowed : "x-dd-drop-ok",
23066     /**
23067      * @cfg {String} dropNotAllowed
23068      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23069      */
23070     dropNotAllowed : "x-dd-drop-nodrop",
23071
23072     /**
23073      * Returns the data object associated with this drag source
23074      * @return {Object} data An object containing arbitrary data
23075      */
23076     getDragData : function(e){
23077         return this.dragData;
23078     },
23079
23080     // private
23081     onDragEnter : function(e, id){
23082         var target = Roo.dd.DragDropMgr.getDDById(id);
23083         this.cachedTarget = target;
23084         if(this.beforeDragEnter(target, e, id) !== false){
23085             if(target.isNotifyTarget){
23086                 var status = target.notifyEnter(this, e, this.dragData);
23087                 this.proxy.setStatus(status);
23088             }else{
23089                 this.proxy.setStatus(this.dropAllowed);
23090             }
23091             
23092             if(this.afterDragEnter){
23093                 /**
23094                  * An empty function by default, but provided so that you can perform a custom action
23095                  * when the dragged item enters the drop target by providing an implementation.
23096                  * @param {Roo.dd.DragDrop} target The drop target
23097                  * @param {Event} e The event object
23098                  * @param {String} id The id of the dragged element
23099                  * @method afterDragEnter
23100                  */
23101                 this.afterDragEnter(target, e, id);
23102             }
23103         }
23104     },
23105
23106     /**
23107      * An empty function by default, but provided so that you can perform a custom action
23108      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23109      * @param {Roo.dd.DragDrop} target The drop target
23110      * @param {Event} e The event object
23111      * @param {String} id The id of the dragged element
23112      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23113      */
23114     beforeDragEnter : function(target, e, id){
23115         return true;
23116     },
23117
23118     // private
23119     alignElWithMouse: function() {
23120         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23121         this.proxy.sync();
23122     },
23123
23124     // private
23125     onDragOver : function(e, id){
23126         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23127         if(this.beforeDragOver(target, e, id) !== false){
23128             if(target.isNotifyTarget){
23129                 var status = target.notifyOver(this, e, this.dragData);
23130                 this.proxy.setStatus(status);
23131             }
23132
23133             if(this.afterDragOver){
23134                 /**
23135                  * An empty function by default, but provided so that you can perform a custom action
23136                  * while the dragged item is over the drop target by providing an implementation.
23137                  * @param {Roo.dd.DragDrop} target The drop target
23138                  * @param {Event} e The event object
23139                  * @param {String} id The id of the dragged element
23140                  * @method afterDragOver
23141                  */
23142                 this.afterDragOver(target, e, id);
23143             }
23144         }
23145     },
23146
23147     /**
23148      * An empty function by default, but provided so that you can perform a custom action
23149      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23150      * @param {Roo.dd.DragDrop} target The drop target
23151      * @param {Event} e The event object
23152      * @param {String} id The id of the dragged element
23153      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23154      */
23155     beforeDragOver : function(target, e, id){
23156         return true;
23157     },
23158
23159     // private
23160     onDragOut : function(e, id){
23161         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23162         if(this.beforeDragOut(target, e, id) !== false){
23163             if(target.isNotifyTarget){
23164                 target.notifyOut(this, e, this.dragData);
23165             }
23166             this.proxy.reset();
23167             if(this.afterDragOut){
23168                 /**
23169                  * An empty function by default, but provided so that you can perform a custom action
23170                  * after the dragged item is dragged out of the target without dropping.
23171                  * @param {Roo.dd.DragDrop} target The drop target
23172                  * @param {Event} e The event object
23173                  * @param {String} id The id of the dragged element
23174                  * @method afterDragOut
23175                  */
23176                 this.afterDragOut(target, e, id);
23177             }
23178         }
23179         this.cachedTarget = null;
23180     },
23181
23182     /**
23183      * An empty function by default, but provided so that you can perform a custom action before the dragged
23184      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23185      * @param {Roo.dd.DragDrop} target The drop target
23186      * @param {Event} e The event object
23187      * @param {String} id The id of the dragged element
23188      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23189      */
23190     beforeDragOut : function(target, e, id){
23191         return true;
23192     },
23193     
23194     // private
23195     onDragDrop : function(e, id){
23196         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23197         if(this.beforeDragDrop(target, e, id) !== false){
23198             if(target.isNotifyTarget){
23199                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23200                     this.onValidDrop(target, e, id);
23201                 }else{
23202                     this.onInvalidDrop(target, e, id);
23203                 }
23204             }else{
23205                 this.onValidDrop(target, e, id);
23206             }
23207             
23208             if(this.afterDragDrop){
23209                 /**
23210                  * An empty function by default, but provided so that you can perform a custom action
23211                  * after a valid drag drop has occurred 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 dropped element
23215                  * @method afterDragDrop
23216                  */
23217                 this.afterDragDrop(target, e, id);
23218             }
23219         }
23220         delete this.cachedTarget;
23221     },
23222
23223     /**
23224      * An empty function by default, but provided so that you can perform a custom action before the dragged
23225      * item is dropped onto the target and optionally cancel the onDragDrop.
23226      * @param {Roo.dd.DragDrop} target The drop target
23227      * @param {Event} e The event object
23228      * @param {String} id The id of the dragged element
23229      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23230      */
23231     beforeDragDrop : function(target, e, id){
23232         return true;
23233     },
23234
23235     // private
23236     onValidDrop : function(target, e, id){
23237         this.hideProxy();
23238         if(this.afterValidDrop){
23239             /**
23240              * An empty function by default, but provided so that you can perform a custom action
23241              * after a valid drop has occurred by providing an implementation.
23242              * @param {Object} target The target DD 
23243              * @param {Event} e The event object
23244              * @param {String} id The id of the dropped element
23245              * @method afterInvalidDrop
23246              */
23247             this.afterValidDrop(target, e, id);
23248         }
23249     },
23250
23251     // private
23252     getRepairXY : function(e, data){
23253         return this.el.getXY();  
23254     },
23255
23256     // private
23257     onInvalidDrop : function(target, e, id){
23258         this.beforeInvalidDrop(target, e, id);
23259         if(this.cachedTarget){
23260             if(this.cachedTarget.isNotifyTarget){
23261                 this.cachedTarget.notifyOut(this, e, this.dragData);
23262             }
23263             this.cacheTarget = null;
23264         }
23265         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23266
23267         if(this.afterInvalidDrop){
23268             /**
23269              * An empty function by default, but provided so that you can perform a custom action
23270              * after an invalid drop has occurred by providing an implementation.
23271              * @param {Event} e The event object
23272              * @param {String} id The id of the dropped element
23273              * @method afterInvalidDrop
23274              */
23275             this.afterInvalidDrop(e, id);
23276         }
23277     },
23278
23279     // private
23280     afterRepair : function(){
23281         if(Roo.enableFx){
23282             this.el.highlight(this.hlColor || "c3daf9");
23283         }
23284         this.dragging = false;
23285     },
23286
23287     /**
23288      * An empty function by default, but provided so that you can perform a custom action after an invalid
23289      * drop has occurred.
23290      * @param {Roo.dd.DragDrop} target The drop target
23291      * @param {Event} e The event object
23292      * @param {String} id The id of the dragged element
23293      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23294      */
23295     beforeInvalidDrop : function(target, e, id){
23296         return true;
23297     },
23298
23299     // private
23300     handleMouseDown : function(e){
23301         if(this.dragging) {
23302             return;
23303         }
23304         var data = this.getDragData(e);
23305         if(data && this.onBeforeDrag(data, e) !== false){
23306             this.dragData = data;
23307             this.proxy.stop();
23308             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23309         } 
23310     },
23311
23312     /**
23313      * An empty function by default, but provided so that you can perform a custom action before the initial
23314      * drag event begins and optionally cancel it.
23315      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23316      * @param {Event} e The event object
23317      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23318      */
23319     onBeforeDrag : function(data, e){
23320         return true;
23321     },
23322
23323     /**
23324      * An empty function by default, but provided so that you can perform a custom action once the initial
23325      * drag event has begun.  The drag cannot be canceled from this function.
23326      * @param {Number} x The x position of the click on the dragged object
23327      * @param {Number} y The y position of the click on the dragged object
23328      */
23329     onStartDrag : Roo.emptyFn,
23330
23331     // private - YUI override
23332     startDrag : function(x, y){
23333         this.proxy.reset();
23334         this.dragging = true;
23335         this.proxy.update("");
23336         this.onInitDrag(x, y);
23337         this.proxy.show();
23338     },
23339
23340     // private
23341     onInitDrag : function(x, y){
23342         var clone = this.el.dom.cloneNode(true);
23343         clone.id = Roo.id(); // prevent duplicate ids
23344         this.proxy.update(clone);
23345         this.onStartDrag(x, y);
23346         return true;
23347     },
23348
23349     /**
23350      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23351      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23352      */
23353     getProxy : function(){
23354         return this.proxy;  
23355     },
23356
23357     /**
23358      * Hides the drag source's {@link Roo.dd.StatusProxy}
23359      */
23360     hideProxy : function(){
23361         this.proxy.hide();  
23362         this.proxy.reset(true);
23363         this.dragging = false;
23364     },
23365
23366     // private
23367     triggerCacheRefresh : function(){
23368         Roo.dd.DDM.refreshCache(this.groups);
23369     },
23370
23371     // private - override to prevent hiding
23372     b4EndDrag: function(e) {
23373     },
23374
23375     // private - override to prevent moving
23376     endDrag : function(e){
23377         this.onEndDrag(this.dragData, e);
23378     },
23379
23380     // private
23381     onEndDrag : function(data, e){
23382     },
23383     
23384     // private - pin to cursor
23385     autoOffset : function(x, y) {
23386         this.setDelta(-12, -20);
23387     }    
23388 });/*
23389  * Based on:
23390  * Ext JS Library 1.1.1
23391  * Copyright(c) 2006-2007, Ext JS, LLC.
23392  *
23393  * Originally Released Under LGPL - original licence link has changed is not relivant.
23394  *
23395  * Fork - LGPL
23396  * <script type="text/javascript">
23397  */
23398
23399
23400 /**
23401  * @class Roo.dd.DropTarget
23402  * @extends Roo.dd.DDTarget
23403  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23404  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23405  * @constructor
23406  * @param {String/HTMLElement/Element} el The container element
23407  * @param {Object} config
23408  */
23409 Roo.dd.DropTarget = function(el, config){
23410     this.el = Roo.get(el);
23411     
23412     var listeners = false; ;
23413     if (config && config.listeners) {
23414         listeners= config.listeners;
23415         delete config.listeners;
23416     }
23417     Roo.apply(this, config);
23418     
23419     if(this.containerScroll){
23420         Roo.dd.ScrollManager.register(this.el);
23421     }
23422     this.addEvents( {
23423          /**
23424          * @scope Roo.dd.DropTarget
23425          */
23426          
23427          /**
23428          * @event enter
23429          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23430          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23431          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23432          * 
23433          * IMPORTANT : it should set  this.valid to true|false
23434          * 
23435          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23436          * @param {Event} e The event
23437          * @param {Object} data An object containing arbitrary data supplied by the drag source
23438          */
23439         "enter" : true,
23440         
23441          /**
23442          * @event over
23443          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23444          * This method will be called on every mouse movement while the drag source is over the drop target.
23445          * This default implementation simply returns the dropAllowed config value.
23446          * 
23447          * IMPORTANT : it should set  this.valid to true|false
23448          * 
23449          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23450          * @param {Event} e The event
23451          * @param {Object} data An object containing arbitrary data supplied by the drag source
23452          
23453          */
23454         "over" : true,
23455         /**
23456          * @event out
23457          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23458          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23459          * overClass (if any) from the drop element.
23460          * 
23461          * 
23462          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23463          * @param {Event} e The event
23464          * @param {Object} data An object containing arbitrary data supplied by the drag source
23465          */
23466          "out" : true,
23467          
23468         /**
23469          * @event drop
23470          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23471          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23472          * implementation that does something to process the drop event and returns true so that the drag source's
23473          * repair action does not run.
23474          * 
23475          * IMPORTANT : it should set this.success
23476          * 
23477          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23478          * @param {Event} e The event
23479          * @param {Object} data An object containing arbitrary data supplied by the drag source
23480         */
23481          "drop" : true
23482     });
23483             
23484      
23485     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23486         this.el.dom, 
23487         this.ddGroup || this.group,
23488         {
23489             isTarget: true,
23490             listeners : listeners || {} 
23491            
23492         
23493         }
23494     );
23495
23496 };
23497
23498 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23499     /**
23500      * @cfg {String} overClass
23501      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23502      */
23503      /**
23504      * @cfg {String} ddGroup
23505      * The drag drop group to handle drop events for
23506      */
23507      
23508     /**
23509      * @cfg {String} dropAllowed
23510      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23511      */
23512     dropAllowed : "x-dd-drop-ok",
23513     /**
23514      * @cfg {String} dropNotAllowed
23515      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23516      */
23517     dropNotAllowed : "x-dd-drop-nodrop",
23518     /**
23519      * @cfg {boolean} success
23520      * set this after drop listener.. 
23521      */
23522     success : false,
23523     /**
23524      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23525      * if the drop point is valid for over/enter..
23526      */
23527     valid : false,
23528     // private
23529     isTarget : true,
23530
23531     // private
23532     isNotifyTarget : true,
23533     
23534     /**
23535      * @hide
23536      */
23537     notifyEnter : function(dd, e, data)
23538     {
23539         this.valid = true;
23540         this.fireEvent('enter', dd, e, data);
23541         if(this.overClass){
23542             this.el.addClass(this.overClass);
23543         }
23544         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23545             this.valid ? this.dropAllowed : this.dropNotAllowed
23546         );
23547     },
23548
23549     /**
23550      * @hide
23551      */
23552     notifyOver : function(dd, e, data)
23553     {
23554         this.valid = true;
23555         this.fireEvent('over', dd, e, data);
23556         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23557             this.valid ? this.dropAllowed : this.dropNotAllowed
23558         );
23559     },
23560
23561     /**
23562      * @hide
23563      */
23564     notifyOut : function(dd, e, data)
23565     {
23566         this.fireEvent('out', dd, e, data);
23567         if(this.overClass){
23568             this.el.removeClass(this.overClass);
23569         }
23570     },
23571
23572     /**
23573      * @hide
23574      */
23575     notifyDrop : function(dd, e, data)
23576     {
23577         this.success = false;
23578         this.fireEvent('drop', dd, e, data);
23579         return this.success;
23580     }
23581 });/*
23582  * Based on:
23583  * Ext JS Library 1.1.1
23584  * Copyright(c) 2006-2007, Ext JS, LLC.
23585  *
23586  * Originally Released Under LGPL - original licence link has changed is not relivant.
23587  *
23588  * Fork - LGPL
23589  * <script type="text/javascript">
23590  */
23591
23592
23593 /**
23594  * @class Roo.dd.DragZone
23595  * @extends Roo.dd.DragSource
23596  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23597  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23598  * @constructor
23599  * @param {String/HTMLElement/Element} el The container element
23600  * @param {Object} config
23601  */
23602 Roo.dd.DragZone = function(el, config){
23603     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23604     if(this.containerScroll){
23605         Roo.dd.ScrollManager.register(this.el);
23606     }
23607 };
23608
23609 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23610     /**
23611      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23612      * for auto scrolling during drag operations.
23613      */
23614     /**
23615      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23616      * method after a failed drop (defaults to "c3daf9" - light blue)
23617      */
23618
23619     /**
23620      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23621      * for a valid target to drag based on the mouse down. Override this method
23622      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23623      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23624      * @param {EventObject} e The mouse down event
23625      * @return {Object} The dragData
23626      */
23627     getDragData : function(e){
23628         return Roo.dd.Registry.getHandleFromEvent(e);
23629     },
23630     
23631     /**
23632      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23633      * this.dragData.ddel
23634      * @param {Number} x The x position of the click on the dragged object
23635      * @param {Number} y The y position of the click on the dragged object
23636      * @return {Boolean} true to continue the drag, false to cancel
23637      */
23638     onInitDrag : function(x, y){
23639         this.proxy.update(this.dragData.ddel.cloneNode(true));
23640         this.onStartDrag(x, y);
23641         return true;
23642     },
23643     
23644     /**
23645      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23646      */
23647     afterRepair : function(){
23648         if(Roo.enableFx){
23649             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23650         }
23651         this.dragging = false;
23652     },
23653
23654     /**
23655      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23656      * the XY of this.dragData.ddel
23657      * @param {EventObject} e The mouse up event
23658      * @return {Array} The xy location (e.g. [100, 200])
23659      */
23660     getRepairXY : function(e){
23661         return Roo.Element.fly(this.dragData.ddel).getXY();  
23662     }
23663 });/*
23664  * Based on:
23665  * Ext JS Library 1.1.1
23666  * Copyright(c) 2006-2007, Ext JS, LLC.
23667  *
23668  * Originally Released Under LGPL - original licence link has changed is not relivant.
23669  *
23670  * Fork - LGPL
23671  * <script type="text/javascript">
23672  */
23673 /**
23674  * @class Roo.dd.DropZone
23675  * @extends Roo.dd.DropTarget
23676  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23677  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23678  * @constructor
23679  * @param {String/HTMLElement/Element} el The container element
23680  * @param {Object} config
23681  */
23682 Roo.dd.DropZone = function(el, config){
23683     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23684 };
23685
23686 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23687     /**
23688      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23689      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23690      * provide your own custom lookup.
23691      * @param {Event} e The event
23692      * @return {Object} data The custom data
23693      */
23694     getTargetFromEvent : function(e){
23695         return Roo.dd.Registry.getTargetFromEvent(e);
23696     },
23697
23698     /**
23699      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23700      * that it has registered.  This method has no default implementation and should be overridden to provide
23701      * node-specific processing if necessary.
23702      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23703      * {@link #getTargetFromEvent} for this node)
23704      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23705      * @param {Event} e The event
23706      * @param {Object} data An object containing arbitrary data supplied by the drag source
23707      */
23708     onNodeEnter : function(n, dd, e, data){
23709         
23710     },
23711
23712     /**
23713      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23714      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23715      * overridden to provide the proper feedback.
23716      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23717      * {@link #getTargetFromEvent} for this node)
23718      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23719      * @param {Event} e The event
23720      * @param {Object} data An object containing arbitrary data supplied by the drag source
23721      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23722      * underlying {@link Roo.dd.StatusProxy} can be updated
23723      */
23724     onNodeOver : function(n, dd, e, data){
23725         return this.dropAllowed;
23726     },
23727
23728     /**
23729      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23730      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23731      * node-specific processing if necessary.
23732      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23733      * {@link #getTargetFromEvent} for this node)
23734      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23735      * @param {Event} e The event
23736      * @param {Object} data An object containing arbitrary data supplied by the drag source
23737      */
23738     onNodeOut : function(n, dd, e, data){
23739         
23740     },
23741
23742     /**
23743      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23744      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23745      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23746      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23747      * {@link #getTargetFromEvent} for this node)
23748      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23749      * @param {Event} e The event
23750      * @param {Object} data An object containing arbitrary data supplied by the drag source
23751      * @return {Boolean} True if the drop was valid, else false
23752      */
23753     onNodeDrop : function(n, dd, e, data){
23754         return false;
23755     },
23756
23757     /**
23758      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23759      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23760      * it should be overridden to provide the proper feedback if necessary.
23761      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23762      * @param {Event} e The event
23763      * @param {Object} data An object containing arbitrary data supplied by the drag source
23764      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23765      * underlying {@link Roo.dd.StatusProxy} can be updated
23766      */
23767     onContainerOver : function(dd, e, data){
23768         return this.dropNotAllowed;
23769     },
23770
23771     /**
23772      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23773      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23774      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23775      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23776      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23777      * @param {Event} e The event
23778      * @param {Object} data An object containing arbitrary data supplied by the drag source
23779      * @return {Boolean} True if the drop was valid, else false
23780      */
23781     onContainerDrop : function(dd, e, data){
23782         return false;
23783     },
23784
23785     /**
23786      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23787      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23788      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23789      * you should override this method and provide a custom implementation.
23790      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23791      * @param {Event} e The event
23792      * @param {Object} data An object containing arbitrary data supplied by the drag source
23793      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23794      * underlying {@link Roo.dd.StatusProxy} can be updated
23795      */
23796     notifyEnter : function(dd, e, data){
23797         return this.dropNotAllowed;
23798     },
23799
23800     /**
23801      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23802      * This method will be called on every mouse movement while the drag source is over the drop zone.
23803      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23804      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23805      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23806      * registered node, it will call {@link #onContainerOver}.
23807      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23808      * @param {Event} e The event
23809      * @param {Object} data An object containing arbitrary data supplied by the drag source
23810      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23811      * underlying {@link Roo.dd.StatusProxy} can be updated
23812      */
23813     notifyOver : function(dd, e, data){
23814         var n = this.getTargetFromEvent(e);
23815         if(!n){ // not over valid drop target
23816             if(this.lastOverNode){
23817                 this.onNodeOut(this.lastOverNode, dd, e, data);
23818                 this.lastOverNode = null;
23819             }
23820             return this.onContainerOver(dd, e, data);
23821         }
23822         if(this.lastOverNode != n){
23823             if(this.lastOverNode){
23824                 this.onNodeOut(this.lastOverNode, dd, e, data);
23825             }
23826             this.onNodeEnter(n, dd, e, data);
23827             this.lastOverNode = n;
23828         }
23829         return this.onNodeOver(n, dd, e, data);
23830     },
23831
23832     /**
23833      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23834      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23835      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23836      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23837      * @param {Event} e The event
23838      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23839      */
23840     notifyOut : function(dd, e, data){
23841         if(this.lastOverNode){
23842             this.onNodeOut(this.lastOverNode, dd, e, data);
23843             this.lastOverNode = null;
23844         }
23845     },
23846
23847     /**
23848      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23849      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23850      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23851      * otherwise it will call {@link #onContainerDrop}.
23852      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23853      * @param {Event} e The event
23854      * @param {Object} data An object containing arbitrary data supplied by the drag source
23855      * @return {Boolean} True if the drop was valid, else false
23856      */
23857     notifyDrop : function(dd, e, data){
23858         if(this.lastOverNode){
23859             this.onNodeOut(this.lastOverNode, dd, e, data);
23860             this.lastOverNode = null;
23861         }
23862         var n = this.getTargetFromEvent(e);
23863         return n ?
23864             this.onNodeDrop(n, dd, e, data) :
23865             this.onContainerDrop(dd, e, data);
23866     },
23867
23868     // private
23869     triggerCacheRefresh : function(){
23870         Roo.dd.DDM.refreshCache(this.groups);
23871     }  
23872 });/*
23873  * Based on:
23874  * Ext JS Library 1.1.1
23875  * Copyright(c) 2006-2007, Ext JS, LLC.
23876  *
23877  * Originally Released Under LGPL - original licence link has changed is not relivant.
23878  *
23879  * Fork - LGPL
23880  * <script type="text/javascript">
23881  */
23882
23883
23884 /**
23885  * @class Roo.data.SortTypes
23886  * @static
23887  * Defines the default sorting (casting?) comparison functions used when sorting data.
23888  */
23889 Roo.data.SortTypes = {
23890     /**
23891      * Default sort that does nothing
23892      * @param {Mixed} s The value being converted
23893      * @return {Mixed} The comparison value
23894      */
23895     none : function(s){
23896         return s;
23897     },
23898     
23899     /**
23900      * The regular expression used to strip tags
23901      * @type {RegExp}
23902      * @property
23903      */
23904     stripTagsRE : /<\/?[^>]+>/gi,
23905     
23906     /**
23907      * Strips all HTML tags to sort on text only
23908      * @param {Mixed} s The value being converted
23909      * @return {String} The comparison value
23910      */
23911     asText : function(s){
23912         return String(s).replace(this.stripTagsRE, "");
23913     },
23914     
23915     /**
23916      * Strips all HTML tags to sort on text only - Case insensitive
23917      * @param {Mixed} s The value being converted
23918      * @return {String} The comparison value
23919      */
23920     asUCText : function(s){
23921         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23922     },
23923     
23924     /**
23925      * Case insensitive string
23926      * @param {Mixed} s The value being converted
23927      * @return {String} The comparison value
23928      */
23929     asUCString : function(s) {
23930         return String(s).toUpperCase();
23931     },
23932     
23933     /**
23934      * Date sorting
23935      * @param {Mixed} s The value being converted
23936      * @return {Number} The comparison value
23937      */
23938     asDate : function(s) {
23939         if(!s){
23940             return 0;
23941         }
23942         if(s instanceof Date){
23943             return s.getTime();
23944         }
23945         return Date.parse(String(s));
23946     },
23947     
23948     /**
23949      * Float sorting
23950      * @param {Mixed} s The value being converted
23951      * @return {Float} The comparison value
23952      */
23953     asFloat : function(s) {
23954         var val = parseFloat(String(s).replace(/,/g, ""));
23955         if(isNaN(val)) {
23956             val = 0;
23957         }
23958         return val;
23959     },
23960     
23961     /**
23962      * Integer sorting
23963      * @param {Mixed} s The value being converted
23964      * @return {Number} The comparison value
23965      */
23966     asInt : function(s) {
23967         var val = parseInt(String(s).replace(/,/g, ""));
23968         if(isNaN(val)) {
23969             val = 0;
23970         }
23971         return val;
23972     }
23973 };/*
23974  * Based on:
23975  * Ext JS Library 1.1.1
23976  * Copyright(c) 2006-2007, Ext JS, LLC.
23977  *
23978  * Originally Released Under LGPL - original licence link has changed is not relivant.
23979  *
23980  * Fork - LGPL
23981  * <script type="text/javascript">
23982  */
23983
23984 /**
23985 * @class Roo.data.Record
23986  * Instances of this class encapsulate both record <em>definition</em> information, and record
23987  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23988  * to access Records cached in an {@link Roo.data.Store} object.<br>
23989  * <p>
23990  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23991  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23992  * objects.<br>
23993  * <p>
23994  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23995  * @constructor
23996  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23997  * {@link #create}. The parameters are the same.
23998  * @param {Array} data An associative Array of data values keyed by the field name.
23999  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24000  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24001  * not specified an integer id is generated.
24002  */
24003 Roo.data.Record = function(data, id){
24004     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24005     this.data = data;
24006 };
24007
24008 /**
24009  * Generate a constructor for a specific record layout.
24010  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24011  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24012  * Each field definition object may contain the following properties: <ul>
24013  * <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,
24014  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24015  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24016  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24017  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24018  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24019  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24020  * this may be omitted.</p></li>
24021  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24022  * <ul><li>auto (Default, implies no conversion)</li>
24023  * <li>string</li>
24024  * <li>int</li>
24025  * <li>float</li>
24026  * <li>boolean</li>
24027  * <li>date</li></ul></p></li>
24028  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24029  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24030  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24031  * by the Reader into an object that will be stored in the Record. It is passed the
24032  * following parameters:<ul>
24033  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24034  * </ul></p></li>
24035  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24036  * </ul>
24037  * <br>usage:<br><pre><code>
24038 var TopicRecord = Roo.data.Record.create(
24039     {name: 'title', mapping: 'topic_title'},
24040     {name: 'author', mapping: 'username'},
24041     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24042     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24043     {name: 'lastPoster', mapping: 'user2'},
24044     {name: 'excerpt', mapping: 'post_text'}
24045 );
24046
24047 var myNewRecord = new TopicRecord({
24048     title: 'Do my job please',
24049     author: 'noobie',
24050     totalPosts: 1,
24051     lastPost: new Date(),
24052     lastPoster: 'Animal',
24053     excerpt: 'No way dude!'
24054 });
24055 myStore.add(myNewRecord);
24056 </code></pre>
24057  * @method create
24058  * @static
24059  */
24060 Roo.data.Record.create = function(o){
24061     var f = function(){
24062         f.superclass.constructor.apply(this, arguments);
24063     };
24064     Roo.extend(f, Roo.data.Record);
24065     var p = f.prototype;
24066     p.fields = new Roo.util.MixedCollection(false, function(field){
24067         return field.name;
24068     });
24069     for(var i = 0, len = o.length; i < len; i++){
24070         p.fields.add(new Roo.data.Field(o[i]));
24071     }
24072     f.getField = function(name){
24073         return p.fields.get(name);  
24074     };
24075     return f;
24076 };
24077
24078 Roo.data.Record.AUTO_ID = 1000;
24079 Roo.data.Record.EDIT = 'edit';
24080 Roo.data.Record.REJECT = 'reject';
24081 Roo.data.Record.COMMIT = 'commit';
24082
24083 Roo.data.Record.prototype = {
24084     /**
24085      * Readonly flag - true if this record has been modified.
24086      * @type Boolean
24087      */
24088     dirty : false,
24089     editing : false,
24090     error: null,
24091     modified: null,
24092
24093     // private
24094     join : function(store){
24095         this.store = store;
24096     },
24097
24098     /**
24099      * Set the named field to the specified value.
24100      * @param {String} name The name of the field to set.
24101      * @param {Object} value The value to set the field to.
24102      */
24103     set : function(name, value){
24104         if(this.data[name] == value){
24105             return;
24106         }
24107         this.dirty = true;
24108         if(!this.modified){
24109             this.modified = {};
24110         }
24111         if(typeof this.modified[name] == 'undefined'){
24112             this.modified[name] = this.data[name];
24113         }
24114         this.data[name] = value;
24115         if(!this.editing && this.store){
24116             this.store.afterEdit(this);
24117         }       
24118     },
24119
24120     /**
24121      * Get the value of the named field.
24122      * @param {String} name The name of the field to get the value of.
24123      * @return {Object} The value of the field.
24124      */
24125     get : function(name){
24126         return this.data[name]; 
24127     },
24128
24129     // private
24130     beginEdit : function(){
24131         this.editing = true;
24132         this.modified = {}; 
24133     },
24134
24135     // private
24136     cancelEdit : function(){
24137         this.editing = false;
24138         delete this.modified;
24139     },
24140
24141     // private
24142     endEdit : function(){
24143         this.editing = false;
24144         if(this.dirty && this.store){
24145             this.store.afterEdit(this);
24146         }
24147     },
24148
24149     /**
24150      * Usually called by the {@link Roo.data.Store} which owns the Record.
24151      * Rejects all changes made to the Record since either creation, or the last commit operation.
24152      * Modified fields are reverted to their original values.
24153      * <p>
24154      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24155      * of reject operations.
24156      */
24157     reject : function(){
24158         var m = this.modified;
24159         for(var n in m){
24160             if(typeof m[n] != "function"){
24161                 this.data[n] = m[n];
24162             }
24163         }
24164         this.dirty = false;
24165         delete this.modified;
24166         this.editing = false;
24167         if(this.store){
24168             this.store.afterReject(this);
24169         }
24170     },
24171
24172     /**
24173      * Usually called by the {@link Roo.data.Store} which owns the Record.
24174      * Commits all changes made to the Record since either creation, or the last commit operation.
24175      * <p>
24176      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24177      * of commit operations.
24178      */
24179     commit : function(){
24180         this.dirty = false;
24181         delete this.modified;
24182         this.editing = false;
24183         if(this.store){
24184             this.store.afterCommit(this);
24185         }
24186     },
24187
24188     // private
24189     hasError : function(){
24190         return this.error != null;
24191     },
24192
24193     // private
24194     clearError : function(){
24195         this.error = null;
24196     },
24197
24198     /**
24199      * Creates a copy of this record.
24200      * @param {String} id (optional) A new record id if you don't want to use this record's id
24201      * @return {Record}
24202      */
24203     copy : function(newId) {
24204         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24205     }
24206 };/*
24207  * Based on:
24208  * Ext JS Library 1.1.1
24209  * Copyright(c) 2006-2007, Ext JS, LLC.
24210  *
24211  * Originally Released Under LGPL - original licence link has changed is not relivant.
24212  *
24213  * Fork - LGPL
24214  * <script type="text/javascript">
24215  */
24216
24217
24218
24219 /**
24220  * @class Roo.data.Store
24221  * @extends Roo.util.Observable
24222  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24223  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24224  * <p>
24225  * 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
24226  * has no knowledge of the format of the data returned by the Proxy.<br>
24227  * <p>
24228  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24229  * instances from the data object. These records are cached and made available through accessor functions.
24230  * @constructor
24231  * Creates a new Store.
24232  * @param {Object} config A config object containing the objects needed for the Store to access data,
24233  * and read the data into Records.
24234  */
24235 Roo.data.Store = function(config){
24236     this.data = new Roo.util.MixedCollection(false);
24237     this.data.getKey = function(o){
24238         return o.id;
24239     };
24240     this.baseParams = {};
24241     // private
24242     this.paramNames = {
24243         "start" : "start",
24244         "limit" : "limit",
24245         "sort" : "sort",
24246         "dir" : "dir",
24247         "multisort" : "_multisort"
24248     };
24249
24250     if(config && config.data){
24251         this.inlineData = config.data;
24252         delete config.data;
24253     }
24254
24255     Roo.apply(this, config);
24256     
24257     if(this.reader){ // reader passed
24258         this.reader = Roo.factory(this.reader, Roo.data);
24259         this.reader.xmodule = this.xmodule || false;
24260         if(!this.recordType){
24261             this.recordType = this.reader.recordType;
24262         }
24263         if(this.reader.onMetaChange){
24264             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24265         }
24266     }
24267
24268     if(this.recordType){
24269         this.fields = this.recordType.prototype.fields;
24270     }
24271     this.modified = [];
24272
24273     this.addEvents({
24274         /**
24275          * @event datachanged
24276          * Fires when the data cache has changed, and a widget which is using this Store
24277          * as a Record cache should refresh its view.
24278          * @param {Store} this
24279          */
24280         datachanged : true,
24281         /**
24282          * @event metachange
24283          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24284          * @param {Store} this
24285          * @param {Object} meta The JSON metadata
24286          */
24287         metachange : true,
24288         /**
24289          * @event add
24290          * Fires when Records have been added to the Store
24291          * @param {Store} this
24292          * @param {Roo.data.Record[]} records The array of Records added
24293          * @param {Number} index The index at which the record(s) were added
24294          */
24295         add : true,
24296         /**
24297          * @event remove
24298          * Fires when a Record has been removed from the Store
24299          * @param {Store} this
24300          * @param {Roo.data.Record} record The Record that was removed
24301          * @param {Number} index The index at which the record was removed
24302          */
24303         remove : true,
24304         /**
24305          * @event update
24306          * Fires when a Record has been updated
24307          * @param {Store} this
24308          * @param {Roo.data.Record} record The Record that was updated
24309          * @param {String} operation The update operation being performed.  Value may be one of:
24310          * <pre><code>
24311  Roo.data.Record.EDIT
24312  Roo.data.Record.REJECT
24313  Roo.data.Record.COMMIT
24314          * </code></pre>
24315          */
24316         update : true,
24317         /**
24318          * @event clear
24319          * Fires when the data cache has been cleared.
24320          * @param {Store} this
24321          */
24322         clear : true,
24323         /**
24324          * @event beforeload
24325          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24326          * the load action will be canceled.
24327          * @param {Store} this
24328          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24329          */
24330         beforeload : true,
24331         /**
24332          * @event beforeloadadd
24333          * Fires after a new set of Records has been loaded.
24334          * @param {Store} this
24335          * @param {Roo.data.Record[]} records The Records that were loaded
24336          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24337          */
24338         beforeloadadd : true,
24339         /**
24340          * @event load
24341          * Fires after a new set of Records has been loaded, before they are added to the store.
24342          * @param {Store} this
24343          * @param {Roo.data.Record[]} records The Records that were loaded
24344          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24345          * @params {Object} return from reader
24346          */
24347         load : true,
24348         /**
24349          * @event loadexception
24350          * Fires if an exception occurs in the Proxy during loading.
24351          * Called with the signature of the Proxy's "loadexception" event.
24352          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24353          * 
24354          * @param {Proxy} 
24355          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24356          * @param {Object} load options 
24357          * @param {Object} jsonData from your request (normally this contains the Exception)
24358          */
24359         loadexception : true
24360     });
24361     
24362     if(this.proxy){
24363         this.proxy = Roo.factory(this.proxy, Roo.data);
24364         this.proxy.xmodule = this.xmodule || false;
24365         this.relayEvents(this.proxy,  ["loadexception"]);
24366     }
24367     this.sortToggle = {};
24368     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24369
24370     Roo.data.Store.superclass.constructor.call(this);
24371
24372     if(this.inlineData){
24373         this.loadData(this.inlineData);
24374         delete this.inlineData;
24375     }
24376 };
24377
24378 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24379      /**
24380     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24381     * without a remote query - used by combo/forms at present.
24382     */
24383     
24384     /**
24385     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24386     */
24387     /**
24388     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24389     */
24390     /**
24391     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24392     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24393     */
24394     /**
24395     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24396     * on any HTTP request
24397     */
24398     /**
24399     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24400     */
24401     /**
24402     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24403     */
24404     multiSort: false,
24405     /**
24406     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24407     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24408     */
24409     remoteSort : false,
24410
24411     /**
24412     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24413      * loaded or when a record is removed. (defaults to false).
24414     */
24415     pruneModifiedRecords : false,
24416
24417     // private
24418     lastOptions : null,
24419
24420     /**
24421      * Add Records to the Store and fires the add event.
24422      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24423      */
24424     add : function(records){
24425         records = [].concat(records);
24426         for(var i = 0, len = records.length; i < len; i++){
24427             records[i].join(this);
24428         }
24429         var index = this.data.length;
24430         this.data.addAll(records);
24431         this.fireEvent("add", this, records, index);
24432     },
24433
24434     /**
24435      * Remove a Record from the Store and fires the remove event.
24436      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24437      */
24438     remove : function(record){
24439         var index = this.data.indexOf(record);
24440         this.data.removeAt(index);
24441  
24442         if(this.pruneModifiedRecords){
24443             this.modified.remove(record);
24444         }
24445         this.fireEvent("remove", this, record, index);
24446     },
24447
24448     /**
24449      * Remove all Records from the Store and fires the clear event.
24450      */
24451     removeAll : function(){
24452         this.data.clear();
24453         if(this.pruneModifiedRecords){
24454             this.modified = [];
24455         }
24456         this.fireEvent("clear", this);
24457     },
24458
24459     /**
24460      * Inserts Records to the Store at the given index and fires the add event.
24461      * @param {Number} index The start index at which to insert the passed Records.
24462      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24463      */
24464     insert : function(index, records){
24465         records = [].concat(records);
24466         for(var i = 0, len = records.length; i < len; i++){
24467             this.data.insert(index, records[i]);
24468             records[i].join(this);
24469         }
24470         this.fireEvent("add", this, records, index);
24471     },
24472
24473     /**
24474      * Get the index within the cache of the passed Record.
24475      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24476      * @return {Number} The index of the passed Record. Returns -1 if not found.
24477      */
24478     indexOf : function(record){
24479         return this.data.indexOf(record);
24480     },
24481
24482     /**
24483      * Get the index within the cache of the Record with the passed id.
24484      * @param {String} id The id of the Record to find.
24485      * @return {Number} The index of the Record. Returns -1 if not found.
24486      */
24487     indexOfId : function(id){
24488         return this.data.indexOfKey(id);
24489     },
24490
24491     /**
24492      * Get the Record with the specified id.
24493      * @param {String} id The id of the Record to find.
24494      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24495      */
24496     getById : function(id){
24497         return this.data.key(id);
24498     },
24499
24500     /**
24501      * Get the Record at the specified index.
24502      * @param {Number} index The index of the Record to find.
24503      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24504      */
24505     getAt : function(index){
24506         return this.data.itemAt(index);
24507     },
24508
24509     /**
24510      * Returns a range of Records between specified indices.
24511      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24512      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24513      * @return {Roo.data.Record[]} An array of Records
24514      */
24515     getRange : function(start, end){
24516         return this.data.getRange(start, end);
24517     },
24518
24519     // private
24520     storeOptions : function(o){
24521         o = Roo.apply({}, o);
24522         delete o.callback;
24523         delete o.scope;
24524         this.lastOptions = o;
24525     },
24526
24527     /**
24528      * Loads the Record cache from the configured Proxy using the configured Reader.
24529      * <p>
24530      * If using remote paging, then the first load call must specify the <em>start</em>
24531      * and <em>limit</em> properties in the options.params property to establish the initial
24532      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24533      * <p>
24534      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24535      * and this call will return before the new data has been loaded. Perform any post-processing
24536      * in a callback function, or in a "load" event handler.</strong>
24537      * <p>
24538      * @param {Object} options An object containing properties which control loading options:<ul>
24539      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24540      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24541      * passed the following arguments:<ul>
24542      * <li>r : Roo.data.Record[]</li>
24543      * <li>options: Options object from the load call</li>
24544      * <li>success: Boolean success indicator</li></ul></li>
24545      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24546      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24547      * </ul>
24548      */
24549     load : function(options){
24550         options = options || {};
24551         if(this.fireEvent("beforeload", this, options) !== false){
24552             this.storeOptions(options);
24553             var p = Roo.apply(options.params || {}, this.baseParams);
24554             // if meta was not loaded from remote source.. try requesting it.
24555             if (!this.reader.metaFromRemote) {
24556                 p._requestMeta = 1;
24557             }
24558             if(this.sortInfo && this.remoteSort){
24559                 var pn = this.paramNames;
24560                 p[pn["sort"]] = this.sortInfo.field;
24561                 p[pn["dir"]] = this.sortInfo.direction;
24562             }
24563             if (this.multiSort) {
24564                 var pn = this.paramNames;
24565                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24566             }
24567             
24568             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24569         }
24570     },
24571
24572     /**
24573      * Reloads the Record cache from the configured Proxy using the configured Reader and
24574      * the options from the last load operation performed.
24575      * @param {Object} options (optional) An object containing properties which may override the options
24576      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24577      * the most recently used options are reused).
24578      */
24579     reload : function(options){
24580         this.load(Roo.applyIf(options||{}, this.lastOptions));
24581     },
24582
24583     // private
24584     // Called as a callback by the Reader during a load operation.
24585     loadRecords : function(o, options, success){
24586          
24587         if(!o){
24588             if(success !== false){
24589                 this.fireEvent("load", this, [], options, o);
24590             }
24591             if(options.callback){
24592                 options.callback.call(options.scope || this, [], options, false);
24593             }
24594             return;
24595         }
24596         // if data returned failure - throw an exception.
24597         if (o.success === false) {
24598             // show a message if no listener is registered.
24599             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24600                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24601             }
24602             // loadmask wil be hooked into this..
24603             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24604             return;
24605         }
24606         var r = o.records, t = o.totalRecords || r.length;
24607         
24608         this.fireEvent("beforeloadadd", this, r, options, o);
24609         
24610         if(!options || options.add !== true){
24611             if(this.pruneModifiedRecords){
24612                 this.modified = [];
24613             }
24614             for(var i = 0, len = r.length; i < len; i++){
24615                 r[i].join(this);
24616             }
24617             if(this.snapshot){
24618                 this.data = this.snapshot;
24619                 delete this.snapshot;
24620             }
24621             this.data.clear();
24622             this.data.addAll(r);
24623             this.totalLength = t;
24624             this.applySort();
24625             this.fireEvent("datachanged", this);
24626         }else{
24627             this.totalLength = Math.max(t, this.data.length+r.length);
24628             this.add(r);
24629         }
24630         
24631         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24632                 
24633             var e = new Roo.data.Record({});
24634
24635             e.set(this.parent.displayField, this.parent.emptyTitle);
24636             e.set(this.parent.valueField, '');
24637
24638             this.insert(0, e);
24639         }
24640             
24641         this.fireEvent("load", this, r, options, o);
24642         if(options.callback){
24643             options.callback.call(options.scope || this, r, options, true);
24644         }
24645     },
24646
24647
24648     /**
24649      * Loads data from a passed data block. A Reader which understands the format of the data
24650      * must have been configured in the constructor.
24651      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24652      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24653      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24654      */
24655     loadData : function(o, append){
24656         var r = this.reader.readRecords(o);
24657         this.loadRecords(r, {add: append}, true);
24658     },
24659     
24660      /**
24661      * using 'cn' the nested child reader read the child array into it's child stores.
24662      * @param {Object} rec The record with a 'children array
24663      */
24664     loadDataFromChildren : function(rec)
24665     {
24666         this.loadData(this.reader.toLoadData(rec));
24667     },
24668     
24669
24670     /**
24671      * Gets the number of cached records.
24672      * <p>
24673      * <em>If using paging, this may not be the total size of the dataset. If the data object
24674      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24675      * the data set size</em>
24676      */
24677     getCount : function(){
24678         return this.data.length || 0;
24679     },
24680
24681     /**
24682      * Gets the total number of records in the dataset as returned by the server.
24683      * <p>
24684      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24685      * the dataset size</em>
24686      */
24687     getTotalCount : function(){
24688         return this.totalLength || 0;
24689     },
24690
24691     /**
24692      * Returns the sort state of the Store as an object with two properties:
24693      * <pre><code>
24694  field {String} The name of the field by which the Records are sorted
24695  direction {String} The sort order, "ASC" or "DESC"
24696      * </code></pre>
24697      */
24698     getSortState : function(){
24699         return this.sortInfo;
24700     },
24701
24702     // private
24703     applySort : function(){
24704         if(this.sortInfo && !this.remoteSort){
24705             var s = this.sortInfo, f = s.field;
24706             var st = this.fields.get(f).sortType;
24707             var fn = function(r1, r2){
24708                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24709                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24710             };
24711             this.data.sort(s.direction, fn);
24712             if(this.snapshot && this.snapshot != this.data){
24713                 this.snapshot.sort(s.direction, fn);
24714             }
24715         }
24716     },
24717
24718     /**
24719      * Sets the default sort column and order to be used by the next load operation.
24720      * @param {String} fieldName The name of the field to sort by.
24721      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24722      */
24723     setDefaultSort : function(field, dir){
24724         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24725     },
24726
24727     /**
24728      * Sort the Records.
24729      * If remote sorting is used, the sort is performed on the server, and the cache is
24730      * reloaded. If local sorting is used, the cache is sorted internally.
24731      * @param {String} fieldName The name of the field to sort by.
24732      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24733      */
24734     sort : function(fieldName, dir){
24735         var f = this.fields.get(fieldName);
24736         if(!dir){
24737             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24738             
24739             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24740                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24741             }else{
24742                 dir = f.sortDir;
24743             }
24744         }
24745         this.sortToggle[f.name] = dir;
24746         this.sortInfo = {field: f.name, direction: dir};
24747         if(!this.remoteSort){
24748             this.applySort();
24749             this.fireEvent("datachanged", this);
24750         }else{
24751             this.load(this.lastOptions);
24752         }
24753     },
24754
24755     /**
24756      * Calls the specified function for each of the Records in the cache.
24757      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24758      * Returning <em>false</em> aborts and exits the iteration.
24759      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24760      */
24761     each : function(fn, scope){
24762         this.data.each(fn, scope);
24763     },
24764
24765     /**
24766      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24767      * (e.g., during paging).
24768      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24769      */
24770     getModifiedRecords : function(){
24771         return this.modified;
24772     },
24773
24774     // private
24775     createFilterFn : function(property, value, anyMatch){
24776         if(!value.exec){ // not a regex
24777             value = String(value);
24778             if(value.length == 0){
24779                 return false;
24780             }
24781             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24782         }
24783         return function(r){
24784             return value.test(r.data[property]);
24785         };
24786     },
24787
24788     /**
24789      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24790      * @param {String} property A field on your records
24791      * @param {Number} start The record index to start at (defaults to 0)
24792      * @param {Number} end The last record index to include (defaults to length - 1)
24793      * @return {Number} The sum
24794      */
24795     sum : function(property, start, end){
24796         var rs = this.data.items, v = 0;
24797         start = start || 0;
24798         end = (end || end === 0) ? end : rs.length-1;
24799
24800         for(var i = start; i <= end; i++){
24801             v += (rs[i].data[property] || 0);
24802         }
24803         return v;
24804     },
24805
24806     /**
24807      * Filter the records by a specified property.
24808      * @param {String} field A field on your records
24809      * @param {String/RegExp} value Either a string that the field
24810      * should start with or a RegExp to test against the field
24811      * @param {Boolean} anyMatch True to match any part not just the beginning
24812      */
24813     filter : function(property, value, anyMatch){
24814         var fn = this.createFilterFn(property, value, anyMatch);
24815         return fn ? this.filterBy(fn) : this.clearFilter();
24816     },
24817
24818     /**
24819      * Filter by a function. The specified function will be called with each
24820      * record in this data source. If the function returns true the record is included,
24821      * otherwise it is filtered.
24822      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24823      * @param {Object} scope (optional) The scope of the function (defaults to this)
24824      */
24825     filterBy : function(fn, scope){
24826         this.snapshot = this.snapshot || this.data;
24827         this.data = this.queryBy(fn, scope||this);
24828         this.fireEvent("datachanged", this);
24829     },
24830
24831     /**
24832      * Query the records by a specified property.
24833      * @param {String} field A field on your records
24834      * @param {String/RegExp} value Either a string that the field
24835      * should start with or a RegExp to test against the field
24836      * @param {Boolean} anyMatch True to match any part not just the beginning
24837      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24838      */
24839     query : function(property, value, anyMatch){
24840         var fn = this.createFilterFn(property, value, anyMatch);
24841         return fn ? this.queryBy(fn) : this.data.clone();
24842     },
24843
24844     /**
24845      * Query by a function. The specified function will be called with each
24846      * record in this data source. If the function returns true the record is included
24847      * in the results.
24848      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24849      * @param {Object} scope (optional) The scope of the function (defaults to this)
24850       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24851      **/
24852     queryBy : function(fn, scope){
24853         var data = this.snapshot || this.data;
24854         return data.filterBy(fn, scope||this);
24855     },
24856
24857     /**
24858      * Collects unique values for a particular dataIndex from this store.
24859      * @param {String} dataIndex The property to collect
24860      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24861      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24862      * @return {Array} An array of the unique values
24863      **/
24864     collect : function(dataIndex, allowNull, bypassFilter){
24865         var d = (bypassFilter === true && this.snapshot) ?
24866                 this.snapshot.items : this.data.items;
24867         var v, sv, r = [], l = {};
24868         for(var i = 0, len = d.length; i < len; i++){
24869             v = d[i].data[dataIndex];
24870             sv = String(v);
24871             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24872                 l[sv] = true;
24873                 r[r.length] = v;
24874             }
24875         }
24876         return r;
24877     },
24878
24879     /**
24880      * Revert to a view of the Record cache with no filtering applied.
24881      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24882      */
24883     clearFilter : function(suppressEvent){
24884         if(this.snapshot && this.snapshot != this.data){
24885             this.data = this.snapshot;
24886             delete this.snapshot;
24887             if(suppressEvent !== true){
24888                 this.fireEvent("datachanged", this);
24889             }
24890         }
24891     },
24892
24893     // private
24894     afterEdit : function(record){
24895         if(this.modified.indexOf(record) == -1){
24896             this.modified.push(record);
24897         }
24898         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24899     },
24900     
24901     // private
24902     afterReject : function(record){
24903         this.modified.remove(record);
24904         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24905     },
24906
24907     // private
24908     afterCommit : function(record){
24909         this.modified.remove(record);
24910         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24911     },
24912
24913     /**
24914      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24915      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24916      */
24917     commitChanges : function(){
24918         var m = this.modified.slice(0);
24919         this.modified = [];
24920         for(var i = 0, len = m.length; i < len; i++){
24921             m[i].commit();
24922         }
24923     },
24924
24925     /**
24926      * Cancel outstanding changes on all changed records.
24927      */
24928     rejectChanges : function(){
24929         var m = this.modified.slice(0);
24930         this.modified = [];
24931         for(var i = 0, len = m.length; i < len; i++){
24932             m[i].reject();
24933         }
24934     },
24935
24936     onMetaChange : function(meta, rtype, o){
24937         this.recordType = rtype;
24938         this.fields = rtype.prototype.fields;
24939         delete this.snapshot;
24940         this.sortInfo = meta.sortInfo || this.sortInfo;
24941         this.modified = [];
24942         this.fireEvent('metachange', this, this.reader.meta);
24943     },
24944     
24945     moveIndex : function(data, type)
24946     {
24947         var index = this.indexOf(data);
24948         
24949         var newIndex = index + type;
24950         
24951         this.remove(data);
24952         
24953         this.insert(newIndex, data);
24954         
24955     }
24956 });/*
24957  * Based on:
24958  * Ext JS Library 1.1.1
24959  * Copyright(c) 2006-2007, Ext JS, LLC.
24960  *
24961  * Originally Released Under LGPL - original licence link has changed is not relivant.
24962  *
24963  * Fork - LGPL
24964  * <script type="text/javascript">
24965  */
24966
24967 /**
24968  * @class Roo.data.SimpleStore
24969  * @extends Roo.data.Store
24970  * Small helper class to make creating Stores from Array data easier.
24971  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24972  * @cfg {Array} fields An array of field definition objects, or field name strings.
24973  * @cfg {Object} an existing reader (eg. copied from another store)
24974  * @cfg {Array} data The multi-dimensional array of data
24975  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24976  * @cfg {Roo.data.Reader} reader  [not-required] 
24977  * @constructor
24978  * @param {Object} config
24979  */
24980 Roo.data.SimpleStore = function(config)
24981 {
24982     Roo.data.SimpleStore.superclass.constructor.call(this, {
24983         isLocal : true,
24984         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24985                 id: config.id
24986             },
24987             Roo.data.Record.create(config.fields)
24988         ),
24989         proxy : new Roo.data.MemoryProxy(config.data)
24990     });
24991     this.load();
24992 };
24993 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24994  * Based on:
24995  * Ext JS Library 1.1.1
24996  * Copyright(c) 2006-2007, Ext JS, LLC.
24997  *
24998  * Originally Released Under LGPL - original licence link has changed is not relivant.
24999  *
25000  * Fork - LGPL
25001  * <script type="text/javascript">
25002  */
25003
25004 /**
25005 /**
25006  * @extends Roo.data.Store
25007  * @class Roo.data.JsonStore
25008  * Small helper class to make creating Stores for JSON data easier. <br/>
25009 <pre><code>
25010 var store = new Roo.data.JsonStore({
25011     url: 'get-images.php',
25012     root: 'images',
25013     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25014 });
25015 </code></pre>
25016  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25017  * JsonReader and HttpProxy (unless inline data is provided).</b>
25018  * @cfg {Array} fields An array of field definition objects, or field name strings.
25019  * @constructor
25020  * @param {Object} config
25021  */
25022 Roo.data.JsonStore = function(c){
25023     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25024         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25025         reader: new Roo.data.JsonReader(c, c.fields)
25026     }));
25027 };
25028 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25029  * Based on:
25030  * Ext JS Library 1.1.1
25031  * Copyright(c) 2006-2007, Ext JS, LLC.
25032  *
25033  * Originally Released Under LGPL - original licence link has changed is not relivant.
25034  *
25035  * Fork - LGPL
25036  * <script type="text/javascript">
25037  */
25038
25039  
25040 Roo.data.Field = function(config){
25041     if(typeof config == "string"){
25042         config = {name: config};
25043     }
25044     Roo.apply(this, config);
25045     
25046     if(!this.type){
25047         this.type = "auto";
25048     }
25049     
25050     var st = Roo.data.SortTypes;
25051     // named sortTypes are supported, here we look them up
25052     if(typeof this.sortType == "string"){
25053         this.sortType = st[this.sortType];
25054     }
25055     
25056     // set default sortType for strings and dates
25057     if(!this.sortType){
25058         switch(this.type){
25059             case "string":
25060                 this.sortType = st.asUCString;
25061                 break;
25062             case "date":
25063                 this.sortType = st.asDate;
25064                 break;
25065             default:
25066                 this.sortType = st.none;
25067         }
25068     }
25069
25070     // define once
25071     var stripRe = /[\$,%]/g;
25072
25073     // prebuilt conversion function for this field, instead of
25074     // switching every time we're reading a value
25075     if(!this.convert){
25076         var cv, dateFormat = this.dateFormat;
25077         switch(this.type){
25078             case "":
25079             case "auto":
25080             case undefined:
25081                 cv = function(v){ return v; };
25082                 break;
25083             case "string":
25084                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25085                 break;
25086             case "int":
25087                 cv = function(v){
25088                     return v !== undefined && v !== null && v !== '' ?
25089                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25090                     };
25091                 break;
25092             case "float":
25093                 cv = function(v){
25094                     return v !== undefined && v !== null && v !== '' ?
25095                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25096                     };
25097                 break;
25098             case "bool":
25099             case "boolean":
25100                 cv = function(v){ return v === true || v === "true" || v == 1; };
25101                 break;
25102             case "date":
25103                 cv = function(v){
25104                     if(!v){
25105                         return '';
25106                     }
25107                     if(v instanceof Date){
25108                         return v;
25109                     }
25110                     if(dateFormat){
25111                         if(dateFormat == "timestamp"){
25112                             return new Date(v*1000);
25113                         }
25114                         return Date.parseDate(v, dateFormat);
25115                     }
25116                     var parsed = Date.parse(v);
25117                     return parsed ? new Date(parsed) : null;
25118                 };
25119              break;
25120             
25121         }
25122         this.convert = cv;
25123     }
25124 };
25125
25126 Roo.data.Field.prototype = {
25127     dateFormat: null,
25128     defaultValue: "",
25129     mapping: null,
25130     sortType : null,
25131     sortDir : "ASC"
25132 };/*
25133  * Based on:
25134  * Ext JS Library 1.1.1
25135  * Copyright(c) 2006-2007, Ext JS, LLC.
25136  *
25137  * Originally Released Under LGPL - original licence link has changed is not relivant.
25138  *
25139  * Fork - LGPL
25140  * <script type="text/javascript">
25141  */
25142  
25143 // Base class for reading structured data from a data source.  This class is intended to be
25144 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25145
25146 /**
25147  * @class Roo.data.DataReader
25148  * @abstract
25149  * Base class for reading structured data from a data source.  This class is intended to be
25150  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25151  */
25152
25153 Roo.data.DataReader = function(meta, recordType){
25154     
25155     this.meta = meta;
25156     
25157     this.recordType = recordType instanceof Array ? 
25158         Roo.data.Record.create(recordType) : recordType;
25159 };
25160
25161 Roo.data.DataReader.prototype = {
25162     
25163     
25164     readerType : 'Data',
25165      /**
25166      * Create an empty record
25167      * @param {Object} data (optional) - overlay some values
25168      * @return {Roo.data.Record} record created.
25169      */
25170     newRow :  function(d) {
25171         var da =  {};
25172         this.recordType.prototype.fields.each(function(c) {
25173             switch( c.type) {
25174                 case 'int' : da[c.name] = 0; break;
25175                 case 'date' : da[c.name] = new Date(); break;
25176                 case 'float' : da[c.name] = 0.0; break;
25177                 case 'boolean' : da[c.name] = false; break;
25178                 default : da[c.name] = ""; break;
25179             }
25180             
25181         });
25182         return new this.recordType(Roo.apply(da, d));
25183     }
25184     
25185     
25186 };/*
25187  * Based on:
25188  * Ext JS Library 1.1.1
25189  * Copyright(c) 2006-2007, Ext JS, LLC.
25190  *
25191  * Originally Released Under LGPL - original licence link has changed is not relivant.
25192  *
25193  * Fork - LGPL
25194  * <script type="text/javascript">
25195  */
25196
25197 /**
25198  * @class Roo.data.DataProxy
25199  * @extends Roo.util.Observable
25200  * @abstract
25201  * This class is an abstract base class for implementations which provide retrieval of
25202  * unformatted data objects.<br>
25203  * <p>
25204  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25205  * (of the appropriate type which knows how to parse the data object) to provide a block of
25206  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25207  * <p>
25208  * Custom implementations must implement the load method as described in
25209  * {@link Roo.data.HttpProxy#load}.
25210  */
25211 Roo.data.DataProxy = function(){
25212     this.addEvents({
25213         /**
25214          * @event beforeload
25215          * Fires before a network request is made to retrieve a data object.
25216          * @param {Object} This DataProxy object.
25217          * @param {Object} params The params parameter to the load function.
25218          */
25219         beforeload : true,
25220         /**
25221          * @event load
25222          * Fires before the load method's callback is called.
25223          * @param {Object} This DataProxy object.
25224          * @param {Object} o The data object.
25225          * @param {Object} arg The callback argument object passed to the load function.
25226          */
25227         load : true,
25228         /**
25229          * @event loadexception
25230          * Fires if an Exception occurs during data retrieval.
25231          * @param {Object} This DataProxy object.
25232          * @param {Object} o The data object.
25233          * @param {Object} arg The callback argument object passed to the load function.
25234          * @param {Object} e The Exception.
25235          */
25236         loadexception : true
25237     });
25238     Roo.data.DataProxy.superclass.constructor.call(this);
25239 };
25240
25241 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25242
25243     /**
25244      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25245      */
25246 /*
25247  * Based on:
25248  * Ext JS Library 1.1.1
25249  * Copyright(c) 2006-2007, Ext JS, LLC.
25250  *
25251  * Originally Released Under LGPL - original licence link has changed is not relivant.
25252  *
25253  * Fork - LGPL
25254  * <script type="text/javascript">
25255  */
25256 /**
25257  * @class Roo.data.MemoryProxy
25258  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25259  * to the Reader when its load method is called.
25260  * @constructor
25261  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25262  */
25263 Roo.data.MemoryProxy = function(data){
25264     if (data.data) {
25265         data = data.data;
25266     }
25267     Roo.data.MemoryProxy.superclass.constructor.call(this);
25268     this.data = data;
25269 };
25270
25271 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25272     
25273     /**
25274      * Load data from the requested source (in this case an in-memory
25275      * data object passed to the constructor), read the data object into
25276      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25277      * process that block using the passed callback.
25278      * @param {Object} params This parameter is not used by the MemoryProxy class.
25279      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25280      * object into a block of Roo.data.Records.
25281      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25282      * The function must be passed <ul>
25283      * <li>The Record block object</li>
25284      * <li>The "arg" argument from the load function</li>
25285      * <li>A boolean success indicator</li>
25286      * </ul>
25287      * @param {Object} scope The scope in which to call the callback
25288      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25289      */
25290     load : function(params, reader, callback, scope, arg){
25291         params = params || {};
25292         var result;
25293         try {
25294             result = reader.readRecords(params.data ? params.data :this.data);
25295         }catch(e){
25296             this.fireEvent("loadexception", this, arg, null, e);
25297             callback.call(scope, null, arg, false);
25298             return;
25299         }
25300         callback.call(scope, result, arg, true);
25301     },
25302     
25303     // private
25304     update : function(params, records){
25305         
25306     }
25307 });/*
25308  * Based on:
25309  * Ext JS Library 1.1.1
25310  * Copyright(c) 2006-2007, Ext JS, LLC.
25311  *
25312  * Originally Released Under LGPL - original licence link has changed is not relivant.
25313  *
25314  * Fork - LGPL
25315  * <script type="text/javascript">
25316  */
25317 /**
25318  * @class Roo.data.HttpProxy
25319  * @extends Roo.data.DataProxy
25320  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25321  * configured to reference a certain URL.<br><br>
25322  * <p>
25323  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25324  * from which the running page was served.<br><br>
25325  * <p>
25326  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25327  * <p>
25328  * Be aware that to enable the browser to parse an XML document, the server must set
25329  * the Content-Type header in the HTTP response to "text/xml".
25330  * @constructor
25331  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25332  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25333  * will be used to make the request.
25334  */
25335 Roo.data.HttpProxy = function(conn){
25336     Roo.data.HttpProxy.superclass.constructor.call(this);
25337     // is conn a conn config or a real conn?
25338     this.conn = conn;
25339     this.useAjax = !conn || !conn.events;
25340   
25341 };
25342
25343 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25344     // thse are take from connection...
25345     
25346     /**
25347      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25348      */
25349     /**
25350      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25351      * extra parameters to each request made by this object. (defaults to undefined)
25352      */
25353     /**
25354      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25355      *  to each request made by this object. (defaults to undefined)
25356      */
25357     /**
25358      * @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)
25359      */
25360     /**
25361      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25362      */
25363      /**
25364      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25365      * @type Boolean
25366      */
25367   
25368
25369     /**
25370      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25371      * @type Boolean
25372      */
25373     /**
25374      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25375      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25376      * a finer-grained basis than the DataProxy events.
25377      */
25378     getConnection : function(){
25379         return this.useAjax ? Roo.Ajax : this.conn;
25380     },
25381
25382     /**
25383      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25384      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25385      * process that block using the passed callback.
25386      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25387      * for the request to the remote server.
25388      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25389      * object into a block of Roo.data.Records.
25390      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25391      * The function must be passed <ul>
25392      * <li>The Record block object</li>
25393      * <li>The "arg" argument from the load function</li>
25394      * <li>A boolean success indicator</li>
25395      * </ul>
25396      * @param {Object} scope The scope in which to call the callback
25397      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25398      */
25399     load : function(params, reader, callback, scope, arg){
25400         if(this.fireEvent("beforeload", this, params) !== false){
25401             var  o = {
25402                 params : params || {},
25403                 request: {
25404                     callback : callback,
25405                     scope : scope,
25406                     arg : arg
25407                 },
25408                 reader: reader,
25409                 callback : this.loadResponse,
25410                 scope: this
25411             };
25412             if(this.useAjax){
25413                 Roo.applyIf(o, this.conn);
25414                 if(this.activeRequest){
25415                     Roo.Ajax.abort(this.activeRequest);
25416                 }
25417                 this.activeRequest = Roo.Ajax.request(o);
25418             }else{
25419                 this.conn.request(o);
25420             }
25421         }else{
25422             callback.call(scope||this, null, arg, false);
25423         }
25424     },
25425
25426     // private
25427     loadResponse : function(o, success, response){
25428         delete this.activeRequest;
25429         if(!success){
25430             this.fireEvent("loadexception", this, o, response);
25431             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25432             return;
25433         }
25434         var result;
25435         try {
25436             result = o.reader.read(response);
25437         }catch(e){
25438             o.success = false;
25439             o.raw = { errorMsg : response.responseText };
25440             this.fireEvent("loadexception", this, o, response, e);
25441             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25442             return;
25443         }
25444         
25445         this.fireEvent("load", this, o, o.request.arg);
25446         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25447     },
25448
25449     // private
25450     update : function(dataSet){
25451
25452     },
25453
25454     // private
25455     updateResponse : function(dataSet){
25456
25457     }
25458 });/*
25459  * Based on:
25460  * Ext JS Library 1.1.1
25461  * Copyright(c) 2006-2007, Ext JS, LLC.
25462  *
25463  * Originally Released Under LGPL - original licence link has changed is not relivant.
25464  *
25465  * Fork - LGPL
25466  * <script type="text/javascript">
25467  */
25468
25469 /**
25470  * @class Roo.data.ScriptTagProxy
25471  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25472  * other than the originating domain of the running page.<br><br>
25473  * <p>
25474  * <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
25475  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25476  * <p>
25477  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25478  * source code that is used as the source inside a &lt;script> tag.<br><br>
25479  * <p>
25480  * In order for the browser to process the returned data, the server must wrap the data object
25481  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25482  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25483  * depending on whether the callback name was passed:
25484  * <p>
25485  * <pre><code>
25486 boolean scriptTag = false;
25487 String cb = request.getParameter("callback");
25488 if (cb != null) {
25489     scriptTag = true;
25490     response.setContentType("text/javascript");
25491 } else {
25492     response.setContentType("application/x-json");
25493 }
25494 Writer out = response.getWriter();
25495 if (scriptTag) {
25496     out.write(cb + "(");
25497 }
25498 out.print(dataBlock.toJsonString());
25499 if (scriptTag) {
25500     out.write(");");
25501 }
25502 </pre></code>
25503  *
25504  * @constructor
25505  * @param {Object} config A configuration object.
25506  */
25507 Roo.data.ScriptTagProxy = function(config){
25508     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25509     Roo.apply(this, config);
25510     this.head = document.getElementsByTagName("head")[0];
25511 };
25512
25513 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25514
25515 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25516     /**
25517      * @cfg {String} url The URL from which to request the data object.
25518      */
25519     /**
25520      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25521      */
25522     timeout : 30000,
25523     /**
25524      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25525      * the server the name of the callback function set up by the load call to process the returned data object.
25526      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25527      * javascript output which calls this named function passing the data object as its only parameter.
25528      */
25529     callbackParam : "callback",
25530     /**
25531      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25532      * name to the request.
25533      */
25534     nocache : true,
25535
25536     /**
25537      * Load data from the configured URL, read the data object into
25538      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25539      * process that block using the passed callback.
25540      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25541      * for the request to the remote server.
25542      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25543      * object into a block of Roo.data.Records.
25544      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25545      * The function must be passed <ul>
25546      * <li>The Record block object</li>
25547      * <li>The "arg" argument from the load function</li>
25548      * <li>A boolean success indicator</li>
25549      * </ul>
25550      * @param {Object} scope The scope in which to call the callback
25551      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25552      */
25553     load : function(params, reader, callback, scope, arg){
25554         if(this.fireEvent("beforeload", this, params) !== false){
25555
25556             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25557
25558             var url = this.url;
25559             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25560             if(this.nocache){
25561                 url += "&_dc=" + (new Date().getTime());
25562             }
25563             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25564             var trans = {
25565                 id : transId,
25566                 cb : "stcCallback"+transId,
25567                 scriptId : "stcScript"+transId,
25568                 params : params,
25569                 arg : arg,
25570                 url : url,
25571                 callback : callback,
25572                 scope : scope,
25573                 reader : reader
25574             };
25575             var conn = this;
25576
25577             window[trans.cb] = function(o){
25578                 conn.handleResponse(o, trans);
25579             };
25580
25581             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25582
25583             if(this.autoAbort !== false){
25584                 this.abort();
25585             }
25586
25587             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25588
25589             var script = document.createElement("script");
25590             script.setAttribute("src", url);
25591             script.setAttribute("type", "text/javascript");
25592             script.setAttribute("id", trans.scriptId);
25593             this.head.appendChild(script);
25594
25595             this.trans = trans;
25596         }else{
25597             callback.call(scope||this, null, arg, false);
25598         }
25599     },
25600
25601     // private
25602     isLoading : function(){
25603         return this.trans ? true : false;
25604     },
25605
25606     /**
25607      * Abort the current server request.
25608      */
25609     abort : function(){
25610         if(this.isLoading()){
25611             this.destroyTrans(this.trans);
25612         }
25613     },
25614
25615     // private
25616     destroyTrans : function(trans, isLoaded){
25617         this.head.removeChild(document.getElementById(trans.scriptId));
25618         clearTimeout(trans.timeoutId);
25619         if(isLoaded){
25620             window[trans.cb] = undefined;
25621             try{
25622                 delete window[trans.cb];
25623             }catch(e){}
25624         }else{
25625             // if hasn't been loaded, wait for load to remove it to prevent script error
25626             window[trans.cb] = function(){
25627                 window[trans.cb] = undefined;
25628                 try{
25629                     delete window[trans.cb];
25630                 }catch(e){}
25631             };
25632         }
25633     },
25634
25635     // private
25636     handleResponse : function(o, trans){
25637         this.trans = false;
25638         this.destroyTrans(trans, true);
25639         var result;
25640         try {
25641             result = trans.reader.readRecords(o);
25642         }catch(e){
25643             this.fireEvent("loadexception", this, o, trans.arg, e);
25644             trans.callback.call(trans.scope||window, null, trans.arg, false);
25645             return;
25646         }
25647         this.fireEvent("load", this, o, trans.arg);
25648         trans.callback.call(trans.scope||window, result, trans.arg, true);
25649     },
25650
25651     // private
25652     handleFailure : function(trans){
25653         this.trans = false;
25654         this.destroyTrans(trans, false);
25655         this.fireEvent("loadexception", this, null, trans.arg);
25656         trans.callback.call(trans.scope||window, null, trans.arg, false);
25657     }
25658 });/*
25659  * Based on:
25660  * Ext JS Library 1.1.1
25661  * Copyright(c) 2006-2007, Ext JS, LLC.
25662  *
25663  * Originally Released Under LGPL - original licence link has changed is not relivant.
25664  *
25665  * Fork - LGPL
25666  * <script type="text/javascript">
25667  */
25668
25669 /**
25670  * @class Roo.data.JsonReader
25671  * @extends Roo.data.DataReader
25672  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25673  * based on mappings in a provided Roo.data.Record constructor.
25674  * 
25675  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25676  * in the reply previously. 
25677  * 
25678  * <p>
25679  * Example code:
25680  * <pre><code>
25681 var RecordDef = Roo.data.Record.create([
25682     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25683     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25684 ]);
25685 var myReader = new Roo.data.JsonReader({
25686     totalProperty: "results",    // The property which contains the total dataset size (optional)
25687     root: "rows",                // The property which contains an Array of row objects
25688     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25689 }, RecordDef);
25690 </code></pre>
25691  * <p>
25692  * This would consume a JSON file like this:
25693  * <pre><code>
25694 { 'results': 2, 'rows': [
25695     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25696     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25697 }
25698 </code></pre>
25699  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25700  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25701  * paged from the remote server.
25702  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25703  * @cfg {String} root name of the property which contains the Array of row objects.
25704  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25705  * @cfg {Array} fields Array of field definition objects
25706  * @constructor
25707  * Create a new JsonReader
25708  * @param {Object} meta Metadata configuration options
25709  * @param {Object} recordType Either an Array of field definition objects,
25710  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25711  */
25712 Roo.data.JsonReader = function(meta, recordType){
25713     
25714     meta = meta || {};
25715     // set some defaults:
25716     Roo.applyIf(meta, {
25717         totalProperty: 'total',
25718         successProperty : 'success',
25719         root : 'data',
25720         id : 'id'
25721     });
25722     
25723     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25724 };
25725 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25726     
25727     readerType : 'Json',
25728     
25729     /**
25730      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25731      * Used by Store query builder to append _requestMeta to params.
25732      * 
25733      */
25734     metaFromRemote : false,
25735     /**
25736      * This method is only used by a DataProxy which has retrieved data from a remote server.
25737      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25738      * @return {Object} data A data block which is used by an Roo.data.Store object as
25739      * a cache of Roo.data.Records.
25740      */
25741     read : function(response){
25742         var json = response.responseText;
25743        
25744         var o = /* eval:var:o */ eval("("+json+")");
25745         if(!o) {
25746             throw {message: "JsonReader.read: Json object not found"};
25747         }
25748         
25749         if(o.metaData){
25750             
25751             delete this.ef;
25752             this.metaFromRemote = true;
25753             this.meta = o.metaData;
25754             this.recordType = Roo.data.Record.create(o.metaData.fields);
25755             this.onMetaChange(this.meta, this.recordType, o);
25756         }
25757         return this.readRecords(o);
25758     },
25759
25760     // private function a store will implement
25761     onMetaChange : function(meta, recordType, o){
25762
25763     },
25764
25765     /**
25766          * @ignore
25767          */
25768     simpleAccess: function(obj, subsc) {
25769         return obj[subsc];
25770     },
25771
25772         /**
25773          * @ignore
25774          */
25775     getJsonAccessor: function(){
25776         var re = /[\[\.]/;
25777         return function(expr) {
25778             try {
25779                 return(re.test(expr))
25780                     ? new Function("obj", "return obj." + expr)
25781                     : function(obj){
25782                         return obj[expr];
25783                     };
25784             } catch(e){}
25785             return Roo.emptyFn;
25786         };
25787     }(),
25788
25789     /**
25790      * Create a data block containing Roo.data.Records from an XML document.
25791      * @param {Object} o An object which contains an Array of row objects in the property specified
25792      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25793      * which contains the total size of the dataset.
25794      * @return {Object} data A data block which is used by an Roo.data.Store object as
25795      * a cache of Roo.data.Records.
25796      */
25797     readRecords : function(o){
25798         /**
25799          * After any data loads, the raw JSON data is available for further custom processing.
25800          * @type Object
25801          */
25802         this.o = o;
25803         var s = this.meta, Record = this.recordType,
25804             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25805
25806 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25807         if (!this.ef) {
25808             if(s.totalProperty) {
25809                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25810                 }
25811                 if(s.successProperty) {
25812                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25813                 }
25814                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25815                 if (s.id) {
25816                         var g = this.getJsonAccessor(s.id);
25817                         this.getId = function(rec) {
25818                                 var r = g(rec);  
25819                                 return (r === undefined || r === "") ? null : r;
25820                         };
25821                 } else {
25822                         this.getId = function(){return null;};
25823                 }
25824             this.ef = [];
25825             for(var jj = 0; jj < fl; jj++){
25826                 f = fi[jj];
25827                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25828                 this.ef[jj] = this.getJsonAccessor(map);
25829             }
25830         }
25831
25832         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25833         if(s.totalProperty){
25834             var vt = parseInt(this.getTotal(o), 10);
25835             if(!isNaN(vt)){
25836                 totalRecords = vt;
25837             }
25838         }
25839         if(s.successProperty){
25840             var vs = this.getSuccess(o);
25841             if(vs === false || vs === 'false'){
25842                 success = false;
25843             }
25844         }
25845         var records = [];
25846         for(var i = 0; i < c; i++){
25847             var n = root[i];
25848             var values = {};
25849             var id = this.getId(n);
25850             for(var j = 0; j < fl; j++){
25851                 f = fi[j];
25852                                 var v = this.ef[j](n);
25853                                 if (!f.convert) {
25854                                         Roo.log('missing convert for ' + f.name);
25855                                         Roo.log(f);
25856                                         continue;
25857                                 }
25858                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25859             }
25860                         if (!Record) {
25861                                 return {
25862                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25863                                         success : false,
25864                                         records : [],
25865                                         totalRecords : 0
25866                                 };
25867                         }
25868             var record = new Record(values, id);
25869             record.json = n;
25870             records[i] = record;
25871         }
25872         return {
25873             raw : o,
25874             success : success,
25875             records : records,
25876             totalRecords : totalRecords
25877         };
25878     },
25879     // used when loading children.. @see loadDataFromChildren
25880     toLoadData: function(rec)
25881     {
25882         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25883         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25884         return { data : data, total : data.length };
25885         
25886     }
25887 });/*
25888  * Based on:
25889  * Ext JS Library 1.1.1
25890  * Copyright(c) 2006-2007, Ext JS, LLC.
25891  *
25892  * Originally Released Under LGPL - original licence link has changed is not relivant.
25893  *
25894  * Fork - LGPL
25895  * <script type="text/javascript">
25896  */
25897
25898 /**
25899  * @class Roo.data.XmlReader
25900  * @extends Roo.data.DataReader
25901  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25902  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25903  * <p>
25904  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25905  * header in the HTTP response must be set to "text/xml".</em>
25906  * <p>
25907  * Example code:
25908  * <pre><code>
25909 var RecordDef = Roo.data.Record.create([
25910    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25911    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25912 ]);
25913 var myReader = new Roo.data.XmlReader({
25914    totalRecords: "results", // The element which contains the total dataset size (optional)
25915    record: "row",           // The repeated element which contains row information
25916    id: "id"                 // The element within the row that provides an ID for the record (optional)
25917 }, RecordDef);
25918 </code></pre>
25919  * <p>
25920  * This would consume an XML file like this:
25921  * <pre><code>
25922 &lt;?xml?>
25923 &lt;dataset>
25924  &lt;results>2&lt;/results>
25925  &lt;row>
25926    &lt;id>1&lt;/id>
25927    &lt;name>Bill&lt;/name>
25928    &lt;occupation>Gardener&lt;/occupation>
25929  &lt;/row>
25930  &lt;row>
25931    &lt;id>2&lt;/id>
25932    &lt;name>Ben&lt;/name>
25933    &lt;occupation>Horticulturalist&lt;/occupation>
25934  &lt;/row>
25935 &lt;/dataset>
25936 </code></pre>
25937  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25938  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25939  * paged from the remote server.
25940  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25941  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25942  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25943  * a record identifier value.
25944  * @constructor
25945  * Create a new XmlReader
25946  * @param {Object} meta Metadata configuration options
25947  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25948  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25949  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25950  */
25951 Roo.data.XmlReader = function(meta, recordType){
25952     meta = meta || {};
25953     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25954 };
25955 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25956     
25957     readerType : 'Xml',
25958     
25959     /**
25960      * This method is only used by a DataProxy which has retrieved data from a remote server.
25961          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25962          * to contain a method called 'responseXML' that returns an XML document object.
25963      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25964      * a cache of Roo.data.Records.
25965      */
25966     read : function(response){
25967         var doc = response.responseXML;
25968         if(!doc) {
25969             throw {message: "XmlReader.read: XML Document not available"};
25970         }
25971         return this.readRecords(doc);
25972     },
25973
25974     /**
25975      * Create a data block containing Roo.data.Records from an XML document.
25976          * @param {Object} doc A parsed XML document.
25977      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25978      * a cache of Roo.data.Records.
25979      */
25980     readRecords : function(doc){
25981         /**
25982          * After any data loads/reads, the raw XML Document is available for further custom processing.
25983          * @type XMLDocument
25984          */
25985         this.xmlData = doc;
25986         var root = doc.documentElement || doc;
25987         var q = Roo.DomQuery;
25988         var recordType = this.recordType, fields = recordType.prototype.fields;
25989         var sid = this.meta.id;
25990         var totalRecords = 0, success = true;
25991         if(this.meta.totalRecords){
25992             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25993         }
25994         
25995         if(this.meta.success){
25996             var sv = q.selectValue(this.meta.success, root, true);
25997             success = sv !== false && sv !== 'false';
25998         }
25999         var records = [];
26000         var ns = q.select(this.meta.record, root);
26001         for(var i = 0, len = ns.length; i < len; i++) {
26002                 var n = ns[i];
26003                 var values = {};
26004                 var id = sid ? q.selectValue(sid, n) : undefined;
26005                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26006                     var f = fields.items[j];
26007                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26008                     v = f.convert(v);
26009                     values[f.name] = v;
26010                 }
26011                 var record = new recordType(values, id);
26012                 record.node = n;
26013                 records[records.length] = record;
26014             }
26015
26016             return {
26017                 success : success,
26018                 records : records,
26019                 totalRecords : totalRecords || records.length
26020             };
26021     }
26022 });/*
26023  * Based on:
26024  * Ext JS Library 1.1.1
26025  * Copyright(c) 2006-2007, Ext JS, LLC.
26026  *
26027  * Originally Released Under LGPL - original licence link has changed is not relivant.
26028  *
26029  * Fork - LGPL
26030  * <script type="text/javascript">
26031  */
26032
26033 /**
26034  * @class Roo.data.ArrayReader
26035  * @extends Roo.data.DataReader
26036  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26037  * Each element of that Array represents a row of data fields. The
26038  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26039  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26040  * <p>
26041  * Example code:.
26042  * <pre><code>
26043 var RecordDef = Roo.data.Record.create([
26044     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26045     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26046 ]);
26047 var myReader = new Roo.data.ArrayReader({
26048     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26049 }, RecordDef);
26050 </code></pre>
26051  * <p>
26052  * This would consume an Array like this:
26053  * <pre><code>
26054 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26055   </code></pre>
26056  
26057  * @constructor
26058  * Create a new JsonReader
26059  * @param {Object} meta Metadata configuration options.
26060  * @param {Object|Array} recordType Either an Array of field definition objects
26061  * 
26062  * @cfg {Array} fields Array of field definition objects
26063  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26064  * as specified to {@link Roo.data.Record#create},
26065  * or an {@link Roo.data.Record} object
26066  *
26067  * 
26068  * created using {@link Roo.data.Record#create}.
26069  */
26070 Roo.data.ArrayReader = function(meta, recordType)
26071 {    
26072     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26073 };
26074
26075 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26076     
26077       /**
26078      * Create a data block containing Roo.data.Records from an XML document.
26079      * @param {Object} o An Array of row objects which represents the dataset.
26080      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26081      * a cache of Roo.data.Records.
26082      */
26083     readRecords : function(o)
26084     {
26085         var sid = this.meta ? this.meta.id : null;
26086         var recordType = this.recordType, fields = recordType.prototype.fields;
26087         var records = [];
26088         var root = o;
26089         for(var i = 0; i < root.length; i++){
26090             var n = root[i];
26091             var values = {};
26092             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26093             for(var j = 0, jlen = fields.length; j < jlen; j++){
26094                 var f = fields.items[j];
26095                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26096                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26097                 v = f.convert(v);
26098                 values[f.name] = v;
26099             }
26100             var record = new recordType(values, id);
26101             record.json = n;
26102             records[records.length] = record;
26103         }
26104         return {
26105             records : records,
26106             totalRecords : records.length
26107         };
26108     },
26109     // used when loading children.. @see loadDataFromChildren
26110     toLoadData: function(rec)
26111     {
26112         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26113         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26114         
26115     }
26116     
26117     
26118 });/*
26119  * Based on:
26120  * Ext JS Library 1.1.1
26121  * Copyright(c) 2006-2007, Ext JS, LLC.
26122  *
26123  * Originally Released Under LGPL - original licence link has changed is not relivant.
26124  *
26125  * Fork - LGPL
26126  * <script type="text/javascript">
26127  */
26128
26129
26130 /**
26131  * @class Roo.data.Tree
26132  * @extends Roo.util.Observable
26133  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26134  * in the tree have most standard DOM functionality.
26135  * @constructor
26136  * @param {Node} root (optional) The root node
26137  */
26138 Roo.data.Tree = function(root){
26139    this.nodeHash = {};
26140    /**
26141     * The root node for this tree
26142     * @type Node
26143     */
26144    this.root = null;
26145    if(root){
26146        this.setRootNode(root);
26147    }
26148    this.addEvents({
26149        /**
26150         * @event append
26151         * Fires when a new child node is appended to a node in this tree.
26152         * @param {Tree} tree The owner tree
26153         * @param {Node} parent The parent node
26154         * @param {Node} node The newly appended node
26155         * @param {Number} index The index of the newly appended node
26156         */
26157        "append" : true,
26158        /**
26159         * @event remove
26160         * Fires when a child node is removed from a node in this tree.
26161         * @param {Tree} tree The owner tree
26162         * @param {Node} parent The parent node
26163         * @param {Node} node The child node removed
26164         */
26165        "remove" : true,
26166        /**
26167         * @event move
26168         * Fires when a node is moved to a new location in the tree
26169         * @param {Tree} tree The owner tree
26170         * @param {Node} node The node moved
26171         * @param {Node} oldParent The old parent of this node
26172         * @param {Node} newParent The new parent of this node
26173         * @param {Number} index The index it was moved to
26174         */
26175        "move" : true,
26176        /**
26177         * @event insert
26178         * Fires when a new child node is inserted in a node in this tree.
26179         * @param {Tree} tree The owner tree
26180         * @param {Node} parent The parent node
26181         * @param {Node} node The child node inserted
26182         * @param {Node} refNode The child node the node was inserted before
26183         */
26184        "insert" : true,
26185        /**
26186         * @event beforeappend
26187         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26188         * @param {Tree} tree The owner tree
26189         * @param {Node} parent The parent node
26190         * @param {Node} node The child node to be appended
26191         */
26192        "beforeappend" : true,
26193        /**
26194         * @event beforeremove
26195         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26196         * @param {Tree} tree The owner tree
26197         * @param {Node} parent The parent node
26198         * @param {Node} node The child node to be removed
26199         */
26200        "beforeremove" : true,
26201        /**
26202         * @event beforemove
26203         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26204         * @param {Tree} tree The owner tree
26205         * @param {Node} node The node being moved
26206         * @param {Node} oldParent The parent of the node
26207         * @param {Node} newParent The new parent the node is moving to
26208         * @param {Number} index The index it is being moved to
26209         */
26210        "beforemove" : true,
26211        /**
26212         * @event beforeinsert
26213         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26214         * @param {Tree} tree The owner tree
26215         * @param {Node} parent The parent node
26216         * @param {Node} node The child node to be inserted
26217         * @param {Node} refNode The child node the node is being inserted before
26218         */
26219        "beforeinsert" : true
26220    });
26221
26222     Roo.data.Tree.superclass.constructor.call(this);
26223 };
26224
26225 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26226     pathSeparator: "/",
26227
26228     proxyNodeEvent : function(){
26229         return this.fireEvent.apply(this, arguments);
26230     },
26231
26232     /**
26233      * Returns the root node for this tree.
26234      * @return {Node}
26235      */
26236     getRootNode : function(){
26237         return this.root;
26238     },
26239
26240     /**
26241      * Sets the root node for this tree.
26242      * @param {Node} node
26243      * @return {Node}
26244      */
26245     setRootNode : function(node){
26246         this.root = node;
26247         node.ownerTree = this;
26248         node.isRoot = true;
26249         this.registerNode(node);
26250         return node;
26251     },
26252
26253     /**
26254      * Gets a node in this tree by its id.
26255      * @param {String} id
26256      * @return {Node}
26257      */
26258     getNodeById : function(id){
26259         return this.nodeHash[id];
26260     },
26261
26262     registerNode : function(node){
26263         this.nodeHash[node.id] = node;
26264     },
26265
26266     unregisterNode : function(node){
26267         delete this.nodeHash[node.id];
26268     },
26269
26270     toString : function(){
26271         return "[Tree"+(this.id?" "+this.id:"")+"]";
26272     }
26273 });
26274
26275 /**
26276  * @class Roo.data.Node
26277  * @extends Roo.util.Observable
26278  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26279  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26280  * @constructor
26281  * @param {Object} attributes The attributes/config for the node
26282  */
26283 Roo.data.Node = function(attributes){
26284     /**
26285      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26286      * @type {Object}
26287      */
26288     this.attributes = attributes || {};
26289     this.leaf = this.attributes.leaf;
26290     /**
26291      * The node id. @type String
26292      */
26293     this.id = this.attributes.id;
26294     if(!this.id){
26295         this.id = Roo.id(null, "ynode-");
26296         this.attributes.id = this.id;
26297     }
26298      
26299     
26300     /**
26301      * All child nodes of this node. @type Array
26302      */
26303     this.childNodes = [];
26304     if(!this.childNodes.indexOf){ // indexOf is a must
26305         this.childNodes.indexOf = function(o){
26306             for(var i = 0, len = this.length; i < len; i++){
26307                 if(this[i] == o) {
26308                     return i;
26309                 }
26310             }
26311             return -1;
26312         };
26313     }
26314     /**
26315      * The parent node for this node. @type Node
26316      */
26317     this.parentNode = null;
26318     /**
26319      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26320      */
26321     this.firstChild = null;
26322     /**
26323      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26324      */
26325     this.lastChild = null;
26326     /**
26327      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26328      */
26329     this.previousSibling = null;
26330     /**
26331      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26332      */
26333     this.nextSibling = null;
26334
26335     this.addEvents({
26336        /**
26337         * @event append
26338         * Fires when a new child node is appended
26339         * @param {Tree} tree The owner tree
26340         * @param {Node} this This node
26341         * @param {Node} node The newly appended node
26342         * @param {Number} index The index of the newly appended node
26343         */
26344        "append" : true,
26345        /**
26346         * @event remove
26347         * Fires when a child node is removed
26348         * @param {Tree} tree The owner tree
26349         * @param {Node} this This node
26350         * @param {Node} node The removed node
26351         */
26352        "remove" : true,
26353        /**
26354         * @event move
26355         * Fires when this node is moved to a new location in the tree
26356         * @param {Tree} tree The owner tree
26357         * @param {Node} this This node
26358         * @param {Node} oldParent The old parent of this node
26359         * @param {Node} newParent The new parent of this node
26360         * @param {Number} index The index it was moved to
26361         */
26362        "move" : true,
26363        /**
26364         * @event insert
26365         * Fires when a new child node is inserted.
26366         * @param {Tree} tree The owner tree
26367         * @param {Node} this This node
26368         * @param {Node} node The child node inserted
26369         * @param {Node} refNode The child node the node was inserted before
26370         */
26371        "insert" : true,
26372        /**
26373         * @event beforeappend
26374         * Fires before a new child is appended, return false to cancel the append.
26375         * @param {Tree} tree The owner tree
26376         * @param {Node} this This node
26377         * @param {Node} node The child node to be appended
26378         */
26379        "beforeappend" : true,
26380        /**
26381         * @event beforeremove
26382         * Fires before a child is removed, return false to cancel the remove.
26383         * @param {Tree} tree The owner tree
26384         * @param {Node} this This node
26385         * @param {Node} node The child node to be removed
26386         */
26387        "beforeremove" : true,
26388        /**
26389         * @event beforemove
26390         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26391         * @param {Tree} tree The owner tree
26392         * @param {Node} this This node
26393         * @param {Node} oldParent The parent of this node
26394         * @param {Node} newParent The new parent this node is moving to
26395         * @param {Number} index The index it is being moved to
26396         */
26397        "beforemove" : true,
26398        /**
26399         * @event beforeinsert
26400         * Fires before a new child is inserted, return false to cancel the insert.
26401         * @param {Tree} tree The owner tree
26402         * @param {Node} this This node
26403         * @param {Node} node The child node to be inserted
26404         * @param {Node} refNode The child node the node is being inserted before
26405         */
26406        "beforeinsert" : true
26407    });
26408     this.listeners = this.attributes.listeners;
26409     Roo.data.Node.superclass.constructor.call(this);
26410 };
26411
26412 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26413     fireEvent : function(evtName){
26414         // first do standard event for this node
26415         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26416             return false;
26417         }
26418         // then bubble it up to the tree if the event wasn't cancelled
26419         var ot = this.getOwnerTree();
26420         if(ot){
26421             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26422                 return false;
26423             }
26424         }
26425         return true;
26426     },
26427
26428     /**
26429      * Returns true if this node is a leaf
26430      * @return {Boolean}
26431      */
26432     isLeaf : function(){
26433         return this.leaf === true;
26434     },
26435
26436     // private
26437     setFirstChild : function(node){
26438         this.firstChild = node;
26439     },
26440
26441     //private
26442     setLastChild : function(node){
26443         this.lastChild = node;
26444     },
26445
26446
26447     /**
26448      * Returns true if this node is the last child of its parent
26449      * @return {Boolean}
26450      */
26451     isLast : function(){
26452        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26453     },
26454
26455     /**
26456      * Returns true if this node is the first child of its parent
26457      * @return {Boolean}
26458      */
26459     isFirst : function(){
26460        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26461     },
26462
26463     hasChildNodes : function(){
26464         return !this.isLeaf() && this.childNodes.length > 0;
26465     },
26466
26467     /**
26468      * Insert node(s) as the last child node of this node.
26469      * @param {Node/Array} node The node or Array of nodes to append
26470      * @return {Node} The appended node if single append, or null if an array was passed
26471      */
26472     appendChild : function(node){
26473         var multi = false;
26474         if(node instanceof Array){
26475             multi = node;
26476         }else if(arguments.length > 1){
26477             multi = arguments;
26478         }
26479         
26480         // if passed an array or multiple args do them one by one
26481         if(multi){
26482             for(var i = 0, len = multi.length; i < len; i++) {
26483                 this.appendChild(multi[i]);
26484             }
26485         }else{
26486             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26487                 return false;
26488             }
26489             var index = this.childNodes.length;
26490             var oldParent = node.parentNode;
26491             // it's a move, make sure we move it cleanly
26492             if(oldParent){
26493                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26494                     return false;
26495                 }
26496                 oldParent.removeChild(node);
26497             }
26498             
26499             index = this.childNodes.length;
26500             if(index == 0){
26501                 this.setFirstChild(node);
26502             }
26503             this.childNodes.push(node);
26504             node.parentNode = this;
26505             var ps = this.childNodes[index-1];
26506             if(ps){
26507                 node.previousSibling = ps;
26508                 ps.nextSibling = node;
26509             }else{
26510                 node.previousSibling = null;
26511             }
26512             node.nextSibling = null;
26513             this.setLastChild(node);
26514             node.setOwnerTree(this.getOwnerTree());
26515             this.fireEvent("append", this.ownerTree, this, node, index);
26516             if(this.ownerTree) {
26517                 this.ownerTree.fireEvent("appendnode", this, node, index);
26518             }
26519             if(oldParent){
26520                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26521             }
26522             return node;
26523         }
26524     },
26525
26526     /**
26527      * Removes a child node from this node.
26528      * @param {Node} node The node to remove
26529      * @return {Node} The removed node
26530      */
26531     removeChild : function(node){
26532         var index = this.childNodes.indexOf(node);
26533         if(index == -1){
26534             return false;
26535         }
26536         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26537             return false;
26538         }
26539
26540         // remove it from childNodes collection
26541         this.childNodes.splice(index, 1);
26542
26543         // update siblings
26544         if(node.previousSibling){
26545             node.previousSibling.nextSibling = node.nextSibling;
26546         }
26547         if(node.nextSibling){
26548             node.nextSibling.previousSibling = node.previousSibling;
26549         }
26550
26551         // update child refs
26552         if(this.firstChild == node){
26553             this.setFirstChild(node.nextSibling);
26554         }
26555         if(this.lastChild == node){
26556             this.setLastChild(node.previousSibling);
26557         }
26558
26559         node.setOwnerTree(null);
26560         // clear any references from the node
26561         node.parentNode = null;
26562         node.previousSibling = null;
26563         node.nextSibling = null;
26564         this.fireEvent("remove", this.ownerTree, this, node);
26565         return node;
26566     },
26567
26568     /**
26569      * Inserts the first node before the second node in this nodes childNodes collection.
26570      * @param {Node} node The node to insert
26571      * @param {Node} refNode The node to insert before (if null the node is appended)
26572      * @return {Node} The inserted node
26573      */
26574     insertBefore : function(node, refNode){
26575         if(!refNode){ // like standard Dom, refNode can be null for append
26576             return this.appendChild(node);
26577         }
26578         // nothing to do
26579         if(node == refNode){
26580             return false;
26581         }
26582
26583         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26584             return false;
26585         }
26586         var index = this.childNodes.indexOf(refNode);
26587         var oldParent = node.parentNode;
26588         var refIndex = index;
26589
26590         // when moving internally, indexes will change after remove
26591         if(oldParent == this && this.childNodes.indexOf(node) < index){
26592             refIndex--;
26593         }
26594
26595         // it's a move, make sure we move it cleanly
26596         if(oldParent){
26597             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26598                 return false;
26599             }
26600             oldParent.removeChild(node);
26601         }
26602         if(refIndex == 0){
26603             this.setFirstChild(node);
26604         }
26605         this.childNodes.splice(refIndex, 0, node);
26606         node.parentNode = this;
26607         var ps = this.childNodes[refIndex-1];
26608         if(ps){
26609             node.previousSibling = ps;
26610             ps.nextSibling = node;
26611         }else{
26612             node.previousSibling = null;
26613         }
26614         node.nextSibling = refNode;
26615         refNode.previousSibling = node;
26616         node.setOwnerTree(this.getOwnerTree());
26617         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26618         if(oldParent){
26619             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26620         }
26621         return node;
26622     },
26623
26624     /**
26625      * Returns the child node at the specified index.
26626      * @param {Number} index
26627      * @return {Node}
26628      */
26629     item : function(index){
26630         return this.childNodes[index];
26631     },
26632
26633     /**
26634      * Replaces one child node in this node with another.
26635      * @param {Node} newChild The replacement node
26636      * @param {Node} oldChild The node to replace
26637      * @return {Node} The replaced node
26638      */
26639     replaceChild : function(newChild, oldChild){
26640         this.insertBefore(newChild, oldChild);
26641         this.removeChild(oldChild);
26642         return oldChild;
26643     },
26644
26645     /**
26646      * Returns the index of a child node
26647      * @param {Node} node
26648      * @return {Number} The index of the node or -1 if it was not found
26649      */
26650     indexOf : function(child){
26651         return this.childNodes.indexOf(child);
26652     },
26653
26654     /**
26655      * Returns the tree this node is in.
26656      * @return {Tree}
26657      */
26658     getOwnerTree : function(){
26659         // if it doesn't have one, look for one
26660         if(!this.ownerTree){
26661             var p = this;
26662             while(p){
26663                 if(p.ownerTree){
26664                     this.ownerTree = p.ownerTree;
26665                     break;
26666                 }
26667                 p = p.parentNode;
26668             }
26669         }
26670         return this.ownerTree;
26671     },
26672
26673     /**
26674      * Returns depth of this node (the root node has a depth of 0)
26675      * @return {Number}
26676      */
26677     getDepth : function(){
26678         var depth = 0;
26679         var p = this;
26680         while(p.parentNode){
26681             ++depth;
26682             p = p.parentNode;
26683         }
26684         return depth;
26685     },
26686
26687     // private
26688     setOwnerTree : function(tree){
26689         // if it's move, we need to update everyone
26690         if(tree != this.ownerTree){
26691             if(this.ownerTree){
26692                 this.ownerTree.unregisterNode(this);
26693             }
26694             this.ownerTree = tree;
26695             var cs = this.childNodes;
26696             for(var i = 0, len = cs.length; i < len; i++) {
26697                 cs[i].setOwnerTree(tree);
26698             }
26699             if(tree){
26700                 tree.registerNode(this);
26701             }
26702         }
26703     },
26704
26705     /**
26706      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26707      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26708      * @return {String} The path
26709      */
26710     getPath : function(attr){
26711         attr = attr || "id";
26712         var p = this.parentNode;
26713         var b = [this.attributes[attr]];
26714         while(p){
26715             b.unshift(p.attributes[attr]);
26716             p = p.parentNode;
26717         }
26718         var sep = this.getOwnerTree().pathSeparator;
26719         return sep + b.join(sep);
26720     },
26721
26722     /**
26723      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26724      * function call will be the scope provided or the current node. The arguments to the function
26725      * will be the args provided or the current node. If the function returns false at any point,
26726      * the bubble is stopped.
26727      * @param {Function} fn The function to call
26728      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26729      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26730      */
26731     bubble : function(fn, scope, args){
26732         var p = this;
26733         while(p){
26734             if(fn.call(scope || p, args || p) === false){
26735                 break;
26736             }
26737             p = p.parentNode;
26738         }
26739     },
26740
26741     /**
26742      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26743      * function call will be the scope provided or the current node. The arguments to the function
26744      * will be the args provided or the current node. If the function returns false at any point,
26745      * the cascade is stopped on that branch.
26746      * @param {Function} fn The function to call
26747      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26748      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26749      */
26750     cascade : function(fn, scope, args){
26751         if(fn.call(scope || this, args || this) !== false){
26752             var cs = this.childNodes;
26753             for(var i = 0, len = cs.length; i < len; i++) {
26754                 cs[i].cascade(fn, scope, args);
26755             }
26756         }
26757     },
26758
26759     /**
26760      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26761      * function call will be the scope provided or the current node. The arguments to the function
26762      * will be the args provided or the current node. If the function returns false at any point,
26763      * the iteration stops.
26764      * @param {Function} fn The function to call
26765      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26766      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26767      */
26768     eachChild : function(fn, scope, args){
26769         var cs = this.childNodes;
26770         for(var i = 0, len = cs.length; i < len; i++) {
26771                 if(fn.call(scope || this, args || cs[i]) === false){
26772                     break;
26773                 }
26774         }
26775     },
26776
26777     /**
26778      * Finds the first child that has the attribute with the specified value.
26779      * @param {String} attribute The attribute name
26780      * @param {Mixed} value The value to search for
26781      * @return {Node} The found child or null if none was found
26782      */
26783     findChild : function(attribute, value){
26784         var cs = this.childNodes;
26785         for(var i = 0, len = cs.length; i < len; i++) {
26786                 if(cs[i].attributes[attribute] == value){
26787                     return cs[i];
26788                 }
26789         }
26790         return null;
26791     },
26792
26793     /**
26794      * Finds the first child by a custom function. The child matches if the function passed
26795      * returns true.
26796      * @param {Function} fn
26797      * @param {Object} scope (optional)
26798      * @return {Node} The found child or null if none was found
26799      */
26800     findChildBy : function(fn, scope){
26801         var cs = this.childNodes;
26802         for(var i = 0, len = cs.length; i < len; i++) {
26803                 if(fn.call(scope||cs[i], cs[i]) === true){
26804                     return cs[i];
26805                 }
26806         }
26807         return null;
26808     },
26809
26810     /**
26811      * Sorts this nodes children using the supplied sort function
26812      * @param {Function} fn
26813      * @param {Object} scope (optional)
26814      */
26815     sort : function(fn, scope){
26816         var cs = this.childNodes;
26817         var len = cs.length;
26818         if(len > 0){
26819             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26820             cs.sort(sortFn);
26821             for(var i = 0; i < len; i++){
26822                 var n = cs[i];
26823                 n.previousSibling = cs[i-1];
26824                 n.nextSibling = cs[i+1];
26825                 if(i == 0){
26826                     this.setFirstChild(n);
26827                 }
26828                 if(i == len-1){
26829                     this.setLastChild(n);
26830                 }
26831             }
26832         }
26833     },
26834
26835     /**
26836      * Returns true if this node is an ancestor (at any point) of the passed node.
26837      * @param {Node} node
26838      * @return {Boolean}
26839      */
26840     contains : function(node){
26841         return node.isAncestor(this);
26842     },
26843
26844     /**
26845      * Returns true if the passed node is an ancestor (at any point) of this node.
26846      * @param {Node} node
26847      * @return {Boolean}
26848      */
26849     isAncestor : function(node){
26850         var p = this.parentNode;
26851         while(p){
26852             if(p == node){
26853                 return true;
26854             }
26855             p = p.parentNode;
26856         }
26857         return false;
26858     },
26859
26860     toString : function(){
26861         return "[Node"+(this.id?" "+this.id:"")+"]";
26862     }
26863 });/*
26864  * Based on:
26865  * Ext JS Library 1.1.1
26866  * Copyright(c) 2006-2007, Ext JS, LLC.
26867  *
26868  * Originally Released Under LGPL - original licence link has changed is not relivant.
26869  *
26870  * Fork - LGPL
26871  * <script type="text/javascript">
26872  */
26873
26874
26875 /**
26876  * @class Roo.Shadow
26877  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26878  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26879  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26880  * @constructor
26881  * Create a new Shadow
26882  * @param {Object} config The config object
26883  */
26884 Roo.Shadow = function(config){
26885     Roo.apply(this, config);
26886     if(typeof this.mode != "string"){
26887         this.mode = this.defaultMode;
26888     }
26889     var o = this.offset, a = {h: 0};
26890     var rad = Math.floor(this.offset/2);
26891     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26892         case "drop":
26893             a.w = 0;
26894             a.l = a.t = o;
26895             a.t -= 1;
26896             if(Roo.isIE){
26897                 a.l -= this.offset + rad;
26898                 a.t -= this.offset + rad;
26899                 a.w -= rad;
26900                 a.h -= rad;
26901                 a.t += 1;
26902             }
26903         break;
26904         case "sides":
26905             a.w = (o*2);
26906             a.l = -o;
26907             a.t = o-1;
26908             if(Roo.isIE){
26909                 a.l -= (this.offset - rad);
26910                 a.t -= this.offset + rad;
26911                 a.l += 1;
26912                 a.w -= (this.offset - rad)*2;
26913                 a.w -= rad + 1;
26914                 a.h -= 1;
26915             }
26916         break;
26917         case "frame":
26918             a.w = a.h = (o*2);
26919             a.l = a.t = -o;
26920             a.t += 1;
26921             a.h -= 2;
26922             if(Roo.isIE){
26923                 a.l -= (this.offset - rad);
26924                 a.t -= (this.offset - rad);
26925                 a.l += 1;
26926                 a.w -= (this.offset + rad + 1);
26927                 a.h -= (this.offset + rad);
26928                 a.h += 1;
26929             }
26930         break;
26931     };
26932
26933     this.adjusts = a;
26934 };
26935
26936 Roo.Shadow.prototype = {
26937     /**
26938      * @cfg {String} mode
26939      * The shadow display mode.  Supports the following options:<br />
26940      * sides: Shadow displays on both sides and bottom only<br />
26941      * frame: Shadow displays equally on all four sides<br />
26942      * drop: Traditional bottom-right drop shadow (default)
26943      */
26944     mode: false,
26945     /**
26946      * @cfg {String} offset
26947      * The number of pixels to offset the shadow from the element (defaults to 4)
26948      */
26949     offset: 4,
26950
26951     // private
26952     defaultMode: "drop",
26953
26954     /**
26955      * Displays the shadow under the target element
26956      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26957      */
26958     show : function(target){
26959         target = Roo.get(target);
26960         if(!this.el){
26961             this.el = Roo.Shadow.Pool.pull();
26962             if(this.el.dom.nextSibling != target.dom){
26963                 this.el.insertBefore(target);
26964             }
26965         }
26966         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26967         if(Roo.isIE){
26968             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26969         }
26970         this.realign(
26971             target.getLeft(true),
26972             target.getTop(true),
26973             target.getWidth(),
26974             target.getHeight()
26975         );
26976         this.el.dom.style.display = "block";
26977     },
26978
26979     /**
26980      * Returns true if the shadow is visible, else false
26981      */
26982     isVisible : function(){
26983         return this.el ? true : false;  
26984     },
26985
26986     /**
26987      * Direct alignment when values are already available. Show must be called at least once before
26988      * calling this method to ensure it is initialized.
26989      * @param {Number} left The target element left position
26990      * @param {Number} top The target element top position
26991      * @param {Number} width The target element width
26992      * @param {Number} height The target element height
26993      */
26994     realign : function(l, t, w, h){
26995         if(!this.el){
26996             return;
26997         }
26998         var a = this.adjusts, d = this.el.dom, s = d.style;
26999         var iea = 0;
27000         s.left = (l+a.l)+"px";
27001         s.top = (t+a.t)+"px";
27002         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27003  
27004         if(s.width != sws || s.height != shs){
27005             s.width = sws;
27006             s.height = shs;
27007             if(!Roo.isIE){
27008                 var cn = d.childNodes;
27009                 var sww = Math.max(0, (sw-12))+"px";
27010                 cn[0].childNodes[1].style.width = sww;
27011                 cn[1].childNodes[1].style.width = sww;
27012                 cn[2].childNodes[1].style.width = sww;
27013                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27014             }
27015         }
27016     },
27017
27018     /**
27019      * Hides this shadow
27020      */
27021     hide : function(){
27022         if(this.el){
27023             this.el.dom.style.display = "none";
27024             Roo.Shadow.Pool.push(this.el);
27025             delete this.el;
27026         }
27027     },
27028
27029     /**
27030      * Adjust the z-index of this shadow
27031      * @param {Number} zindex The new z-index
27032      */
27033     setZIndex : function(z){
27034         this.zIndex = z;
27035         if(this.el){
27036             this.el.setStyle("z-index", z);
27037         }
27038     }
27039 };
27040
27041 // Private utility class that manages the internal Shadow cache
27042 Roo.Shadow.Pool = function(){
27043     var p = [];
27044     var markup = Roo.isIE ?
27045                  '<div class="x-ie-shadow"></div>' :
27046                  '<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>';
27047     return {
27048         pull : function(){
27049             var sh = p.shift();
27050             if(!sh){
27051                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27052                 sh.autoBoxAdjust = false;
27053             }
27054             return sh;
27055         },
27056
27057         push : function(sh){
27058             p.push(sh);
27059         }
27060     };
27061 }();/*
27062  * Based on:
27063  * Ext JS Library 1.1.1
27064  * Copyright(c) 2006-2007, Ext JS, LLC.
27065  *
27066  * Originally Released Under LGPL - original licence link has changed is not relivant.
27067  *
27068  * Fork - LGPL
27069  * <script type="text/javascript">
27070  */
27071
27072
27073 /**
27074  * @class Roo.SplitBar
27075  * @extends Roo.util.Observable
27076  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27077  * <br><br>
27078  * Usage:
27079  * <pre><code>
27080 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27081                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27082 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27083 split.minSize = 100;
27084 split.maxSize = 600;
27085 split.animate = true;
27086 split.on('moved', splitterMoved);
27087 </code></pre>
27088  * @constructor
27089  * Create a new SplitBar
27090  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27091  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27092  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27093  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27094                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27095                         position of the SplitBar).
27096  */
27097 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27098     
27099     /** @private */
27100     this.el = Roo.get(dragElement, true);
27101     this.el.dom.unselectable = "on";
27102     /** @private */
27103     this.resizingEl = Roo.get(resizingElement, true);
27104
27105     /**
27106      * @private
27107      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27108      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27109      * @type Number
27110      */
27111     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27112     
27113     /**
27114      * The minimum size of the resizing element. (Defaults to 0)
27115      * @type Number
27116      */
27117     this.minSize = 0;
27118     
27119     /**
27120      * The maximum size of the resizing element. (Defaults to 2000)
27121      * @type Number
27122      */
27123     this.maxSize = 2000;
27124     
27125     /**
27126      * Whether to animate the transition to the new size
27127      * @type Boolean
27128      */
27129     this.animate = false;
27130     
27131     /**
27132      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27133      * @type Boolean
27134      */
27135     this.useShim = false;
27136     
27137     /** @private */
27138     this.shim = null;
27139     
27140     if(!existingProxy){
27141         /** @private */
27142         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27143     }else{
27144         this.proxy = Roo.get(existingProxy).dom;
27145     }
27146     /** @private */
27147     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27148     
27149     /** @private */
27150     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27151     
27152     /** @private */
27153     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27154     
27155     /** @private */
27156     this.dragSpecs = {};
27157     
27158     /**
27159      * @private The adapter to use to positon and resize elements
27160      */
27161     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27162     this.adapter.init(this);
27163     
27164     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27165         /** @private */
27166         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27167         this.el.addClass("x-splitbar-h");
27168     }else{
27169         /** @private */
27170         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27171         this.el.addClass("x-splitbar-v");
27172     }
27173     
27174     this.addEvents({
27175         /**
27176          * @event resize
27177          * Fires when the splitter is moved (alias for {@link #event-moved})
27178          * @param {Roo.SplitBar} this
27179          * @param {Number} newSize the new width or height
27180          */
27181         "resize" : true,
27182         /**
27183          * @event moved
27184          * Fires when the splitter is moved
27185          * @param {Roo.SplitBar} this
27186          * @param {Number} newSize the new width or height
27187          */
27188         "moved" : true,
27189         /**
27190          * @event beforeresize
27191          * Fires before the splitter is dragged
27192          * @param {Roo.SplitBar} this
27193          */
27194         "beforeresize" : true,
27195
27196         "beforeapply" : true
27197     });
27198
27199     Roo.util.Observable.call(this);
27200 };
27201
27202 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27203     onStartProxyDrag : function(x, y){
27204         this.fireEvent("beforeresize", this);
27205         if(!this.overlay){
27206             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27207             o.unselectable();
27208             o.enableDisplayMode("block");
27209             // all splitbars share the same overlay
27210             Roo.SplitBar.prototype.overlay = o;
27211         }
27212         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27213         this.overlay.show();
27214         Roo.get(this.proxy).setDisplayed("block");
27215         var size = this.adapter.getElementSize(this);
27216         this.activeMinSize = this.getMinimumSize();;
27217         this.activeMaxSize = this.getMaximumSize();;
27218         var c1 = size - this.activeMinSize;
27219         var c2 = Math.max(this.activeMaxSize - size, 0);
27220         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27221             this.dd.resetConstraints();
27222             this.dd.setXConstraint(
27223                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27224                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27225             );
27226             this.dd.setYConstraint(0, 0);
27227         }else{
27228             this.dd.resetConstraints();
27229             this.dd.setXConstraint(0, 0);
27230             this.dd.setYConstraint(
27231                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27232                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27233             );
27234          }
27235         this.dragSpecs.startSize = size;
27236         this.dragSpecs.startPoint = [x, y];
27237         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27238     },
27239     
27240     /** 
27241      * @private Called after the drag operation by the DDProxy
27242      */
27243     onEndProxyDrag : function(e){
27244         Roo.get(this.proxy).setDisplayed(false);
27245         var endPoint = Roo.lib.Event.getXY(e);
27246         if(this.overlay){
27247             this.overlay.hide();
27248         }
27249         var newSize;
27250         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27251             newSize = this.dragSpecs.startSize + 
27252                 (this.placement == Roo.SplitBar.LEFT ?
27253                     endPoint[0] - this.dragSpecs.startPoint[0] :
27254                     this.dragSpecs.startPoint[0] - endPoint[0]
27255                 );
27256         }else{
27257             newSize = this.dragSpecs.startSize + 
27258                 (this.placement == Roo.SplitBar.TOP ?
27259                     endPoint[1] - this.dragSpecs.startPoint[1] :
27260                     this.dragSpecs.startPoint[1] - endPoint[1]
27261                 );
27262         }
27263         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27264         if(newSize != this.dragSpecs.startSize){
27265             if(this.fireEvent('beforeapply', this, newSize) !== false){
27266                 this.adapter.setElementSize(this, newSize);
27267                 this.fireEvent("moved", this, newSize);
27268                 this.fireEvent("resize", this, newSize);
27269             }
27270         }
27271     },
27272     
27273     /**
27274      * Get the adapter this SplitBar uses
27275      * @return The adapter object
27276      */
27277     getAdapter : function(){
27278         return this.adapter;
27279     },
27280     
27281     /**
27282      * Set the adapter this SplitBar uses
27283      * @param {Object} adapter A SplitBar adapter object
27284      */
27285     setAdapter : function(adapter){
27286         this.adapter = adapter;
27287         this.adapter.init(this);
27288     },
27289     
27290     /**
27291      * Gets the minimum size for the resizing element
27292      * @return {Number} The minimum size
27293      */
27294     getMinimumSize : function(){
27295         return this.minSize;
27296     },
27297     
27298     /**
27299      * Sets the minimum size for the resizing element
27300      * @param {Number} minSize The minimum size
27301      */
27302     setMinimumSize : function(minSize){
27303         this.minSize = minSize;
27304     },
27305     
27306     /**
27307      * Gets the maximum size for the resizing element
27308      * @return {Number} The maximum size
27309      */
27310     getMaximumSize : function(){
27311         return this.maxSize;
27312     },
27313     
27314     /**
27315      * Sets the maximum size for the resizing element
27316      * @param {Number} maxSize The maximum size
27317      */
27318     setMaximumSize : function(maxSize){
27319         this.maxSize = maxSize;
27320     },
27321     
27322     /**
27323      * Sets the initialize size for the resizing element
27324      * @param {Number} size The initial size
27325      */
27326     setCurrentSize : function(size){
27327         var oldAnimate = this.animate;
27328         this.animate = false;
27329         this.adapter.setElementSize(this, size);
27330         this.animate = oldAnimate;
27331     },
27332     
27333     /**
27334      * Destroy this splitbar. 
27335      * @param {Boolean} removeEl True to remove the element
27336      */
27337     destroy : function(removeEl){
27338         if(this.shim){
27339             this.shim.remove();
27340         }
27341         this.dd.unreg();
27342         this.proxy.parentNode.removeChild(this.proxy);
27343         if(removeEl){
27344             this.el.remove();
27345         }
27346     }
27347 });
27348
27349 /**
27350  * @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.
27351  */
27352 Roo.SplitBar.createProxy = function(dir){
27353     var proxy = new Roo.Element(document.createElement("div"));
27354     proxy.unselectable();
27355     var cls = 'x-splitbar-proxy';
27356     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27357     document.body.appendChild(proxy.dom);
27358     return proxy.dom;
27359 };
27360
27361 /** 
27362  * @class Roo.SplitBar.BasicLayoutAdapter
27363  * Default Adapter. It assumes the splitter and resizing element are not positioned
27364  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27365  */
27366 Roo.SplitBar.BasicLayoutAdapter = function(){
27367 };
27368
27369 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27370     // do nothing for now
27371     init : function(s){
27372     
27373     },
27374     /**
27375      * Called before drag operations to get the current size of the resizing element. 
27376      * @param {Roo.SplitBar} s The SplitBar using this adapter
27377      */
27378      getElementSize : function(s){
27379         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27380             return s.resizingEl.getWidth();
27381         }else{
27382             return s.resizingEl.getHeight();
27383         }
27384     },
27385     
27386     /**
27387      * Called after drag operations to set the size of the resizing element.
27388      * @param {Roo.SplitBar} s The SplitBar using this adapter
27389      * @param {Number} newSize The new size to set
27390      * @param {Function} onComplete A function to be invoked when resizing is complete
27391      */
27392     setElementSize : function(s, newSize, onComplete){
27393         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27394             if(!s.animate){
27395                 s.resizingEl.setWidth(newSize);
27396                 if(onComplete){
27397                     onComplete(s, newSize);
27398                 }
27399             }else{
27400                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27401             }
27402         }else{
27403             
27404             if(!s.animate){
27405                 s.resizingEl.setHeight(newSize);
27406                 if(onComplete){
27407                     onComplete(s, newSize);
27408                 }
27409             }else{
27410                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27411             }
27412         }
27413     }
27414 };
27415
27416 /** 
27417  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27418  * @extends Roo.SplitBar.BasicLayoutAdapter
27419  * Adapter that  moves the splitter element to align with the resized sizing element. 
27420  * Used with an absolute positioned SplitBar.
27421  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27422  * document.body, make sure you assign an id to the body element.
27423  */
27424 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27425     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27426     this.container = Roo.get(container);
27427 };
27428
27429 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27430     init : function(s){
27431         this.basic.init(s);
27432     },
27433     
27434     getElementSize : function(s){
27435         return this.basic.getElementSize(s);
27436     },
27437     
27438     setElementSize : function(s, newSize, onComplete){
27439         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27440     },
27441     
27442     moveSplitter : function(s){
27443         var yes = Roo.SplitBar;
27444         switch(s.placement){
27445             case yes.LEFT:
27446                 s.el.setX(s.resizingEl.getRight());
27447                 break;
27448             case yes.RIGHT:
27449                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27450                 break;
27451             case yes.TOP:
27452                 s.el.setY(s.resizingEl.getBottom());
27453                 break;
27454             case yes.BOTTOM:
27455                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27456                 break;
27457         }
27458     }
27459 };
27460
27461 /**
27462  * Orientation constant - Create a vertical SplitBar
27463  * @static
27464  * @type Number
27465  */
27466 Roo.SplitBar.VERTICAL = 1;
27467
27468 /**
27469  * Orientation constant - Create a horizontal SplitBar
27470  * @static
27471  * @type Number
27472  */
27473 Roo.SplitBar.HORIZONTAL = 2;
27474
27475 /**
27476  * Placement constant - The resizing element is to the left of the splitter element
27477  * @static
27478  * @type Number
27479  */
27480 Roo.SplitBar.LEFT = 1;
27481
27482 /**
27483  * Placement constant - The resizing element is to the right of the splitter element
27484  * @static
27485  * @type Number
27486  */
27487 Roo.SplitBar.RIGHT = 2;
27488
27489 /**
27490  * Placement constant - The resizing element is positioned above the splitter element
27491  * @static
27492  * @type Number
27493  */
27494 Roo.SplitBar.TOP = 3;
27495
27496 /**
27497  * Placement constant - The resizing element is positioned under splitter element
27498  * @static
27499  * @type Number
27500  */
27501 Roo.SplitBar.BOTTOM = 4;
27502 /*
27503  * Based on:
27504  * Ext JS Library 1.1.1
27505  * Copyright(c) 2006-2007, Ext JS, LLC.
27506  *
27507  * Originally Released Under LGPL - original licence link has changed is not relivant.
27508  *
27509  * Fork - LGPL
27510  * <script type="text/javascript">
27511  */
27512
27513 /**
27514  * @class Roo.View
27515  * @extends Roo.util.Observable
27516  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27517  * This class also supports single and multi selection modes. <br>
27518  * Create a data model bound view:
27519  <pre><code>
27520  var store = new Roo.data.Store(...);
27521
27522  var view = new Roo.View({
27523     el : "my-element",
27524     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27525  
27526     singleSelect: true,
27527     selectedClass: "ydataview-selected",
27528     store: store
27529  });
27530
27531  // listen for node click?
27532  view.on("click", function(vw, index, node, e){
27533  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27534  });
27535
27536  // load XML data
27537  dataModel.load("foobar.xml");
27538  </code></pre>
27539  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27540  * <br><br>
27541  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27542  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27543  * 
27544  * Note: old style constructor is still suported (container, template, config)
27545  * 
27546  * @constructor
27547  * Create a new View
27548  * @param {Object} config The config object
27549  * 
27550  */
27551 Roo.View = function(config, depreciated_tpl, depreciated_config){
27552     
27553     this.parent = false;
27554     
27555     if (typeof(depreciated_tpl) == 'undefined') {
27556         // new way.. - universal constructor.
27557         Roo.apply(this, config);
27558         this.el  = Roo.get(this.el);
27559     } else {
27560         // old format..
27561         this.el  = Roo.get(config);
27562         this.tpl = depreciated_tpl;
27563         Roo.apply(this, depreciated_config);
27564     }
27565     this.wrapEl  = this.el.wrap().wrap();
27566     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27567     
27568     
27569     if(typeof(this.tpl) == "string"){
27570         this.tpl = new Roo.Template(this.tpl);
27571     } else {
27572         // support xtype ctors..
27573         this.tpl = new Roo.factory(this.tpl, Roo);
27574     }
27575     
27576     
27577     this.tpl.compile();
27578     
27579     /** @private */
27580     this.addEvents({
27581         /**
27582          * @event beforeclick
27583          * Fires before a click is processed. Returns false to cancel the default action.
27584          * @param {Roo.View} this
27585          * @param {Number} index The index of the target node
27586          * @param {HTMLElement} node The target node
27587          * @param {Roo.EventObject} e The raw event object
27588          */
27589             "beforeclick" : true,
27590         /**
27591          * @event click
27592          * Fires when a template node is clicked.
27593          * @param {Roo.View} this
27594          * @param {Number} index The index of the target node
27595          * @param {HTMLElement} node The target node
27596          * @param {Roo.EventObject} e The raw event object
27597          */
27598             "click" : true,
27599         /**
27600          * @event dblclick
27601          * Fires when a template node is double clicked.
27602          * @param {Roo.View} this
27603          * @param {Number} index The index of the target node
27604          * @param {HTMLElement} node The target node
27605          * @param {Roo.EventObject} e The raw event object
27606          */
27607             "dblclick" : true,
27608         /**
27609          * @event contextmenu
27610          * Fires when a template node is right clicked.
27611          * @param {Roo.View} this
27612          * @param {Number} index The index of the target node
27613          * @param {HTMLElement} node The target node
27614          * @param {Roo.EventObject} e The raw event object
27615          */
27616             "contextmenu" : true,
27617         /**
27618          * @event selectionchange
27619          * Fires when the selected nodes change.
27620          * @param {Roo.View} this
27621          * @param {Array} selections Array of the selected nodes
27622          */
27623             "selectionchange" : true,
27624     
27625         /**
27626          * @event beforeselect
27627          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27628          * @param {Roo.View} this
27629          * @param {HTMLElement} node The node to be selected
27630          * @param {Array} selections Array of currently selected nodes
27631          */
27632             "beforeselect" : true,
27633         /**
27634          * @event preparedata
27635          * Fires on every row to render, to allow you to change the data.
27636          * @param {Roo.View} this
27637          * @param {Object} data to be rendered (change this)
27638          */
27639           "preparedata" : true
27640           
27641           
27642         });
27643
27644
27645
27646     this.el.on({
27647         "click": this.onClick,
27648         "dblclick": this.onDblClick,
27649         "contextmenu": this.onContextMenu,
27650         scope:this
27651     });
27652
27653     this.selections = [];
27654     this.nodes = [];
27655     this.cmp = new Roo.CompositeElementLite([]);
27656     if(this.store){
27657         this.store = Roo.factory(this.store, Roo.data);
27658         this.setStore(this.store, true);
27659     }
27660     
27661     if ( this.footer && this.footer.xtype) {
27662            
27663          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27664         
27665         this.footer.dataSource = this.store;
27666         this.footer.container = fctr;
27667         this.footer = Roo.factory(this.footer, Roo);
27668         fctr.insertFirst(this.el);
27669         
27670         // this is a bit insane - as the paging toolbar seems to detach the el..
27671 //        dom.parentNode.parentNode.parentNode
27672          // they get detached?
27673     }
27674     
27675     
27676     Roo.View.superclass.constructor.call(this);
27677     
27678     
27679 };
27680
27681 Roo.extend(Roo.View, Roo.util.Observable, {
27682     
27683      /**
27684      * @cfg {Roo.data.Store} store Data store to load data from.
27685      */
27686     store : false,
27687     
27688     /**
27689      * @cfg {String|Roo.Element} el The container element.
27690      */
27691     el : '',
27692     
27693     /**
27694      * @cfg {String|Roo.Template} tpl The template used by this View 
27695      */
27696     tpl : false,
27697     /**
27698      * @cfg {String} dataName the named area of the template to use as the data area
27699      *                          Works with domtemplates roo-name="name"
27700      */
27701     dataName: false,
27702     /**
27703      * @cfg {String} selectedClass The css class to add to selected nodes
27704      */
27705     selectedClass : "x-view-selected",
27706      /**
27707      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27708      */
27709     emptyText : "",
27710     
27711     /**
27712      * @cfg {String} text to display on mask (default Loading)
27713      */
27714     mask : false,
27715     /**
27716      * @cfg {Boolean} multiSelect Allow multiple selection
27717      */
27718     multiSelect : false,
27719     /**
27720      * @cfg {Boolean} singleSelect Allow single selection
27721      */
27722     singleSelect:  false,
27723     
27724     /**
27725      * @cfg {Boolean} toggleSelect - selecting 
27726      */
27727     toggleSelect : false,
27728     
27729     /**
27730      * @cfg {Boolean} tickable - selecting 
27731      */
27732     tickable : false,
27733     
27734     /**
27735      * Returns the element this view is bound to.
27736      * @return {Roo.Element}
27737      */
27738     getEl : function(){
27739         return this.wrapEl;
27740     },
27741     
27742     
27743
27744     /**
27745      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27746      */
27747     refresh : function(){
27748         //Roo.log('refresh');
27749         var t = this.tpl;
27750         
27751         // if we are using something like 'domtemplate', then
27752         // the what gets used is:
27753         // t.applySubtemplate(NAME, data, wrapping data..)
27754         // the outer template then get' applied with
27755         //     the store 'extra data'
27756         // and the body get's added to the
27757         //      roo-name="data" node?
27758         //      <span class='roo-tpl-{name}'></span> ?????
27759         
27760         
27761         
27762         this.clearSelections();
27763         this.el.update("");
27764         var html = [];
27765         var records = this.store.getRange();
27766         if(records.length < 1) {
27767             
27768             // is this valid??  = should it render a template??
27769             
27770             this.el.update(this.emptyText);
27771             return;
27772         }
27773         var el = this.el;
27774         if (this.dataName) {
27775             this.el.update(t.apply(this.store.meta)); //????
27776             el = this.el.child('.roo-tpl-' + this.dataName);
27777         }
27778         
27779         for(var i = 0, len = records.length; i < len; i++){
27780             var data = this.prepareData(records[i].data, i, records[i]);
27781             this.fireEvent("preparedata", this, data, i, records[i]);
27782             
27783             var d = Roo.apply({}, data);
27784             
27785             if(this.tickable){
27786                 Roo.apply(d, {'roo-id' : Roo.id()});
27787                 
27788                 var _this = this;
27789             
27790                 Roo.each(this.parent.item, function(item){
27791                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27792                         return;
27793                     }
27794                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27795                 });
27796             }
27797             
27798             html[html.length] = Roo.util.Format.trim(
27799                 this.dataName ?
27800                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27801                     t.apply(d)
27802             );
27803         }
27804         
27805         
27806         
27807         el.update(html.join(""));
27808         this.nodes = el.dom.childNodes;
27809         this.updateIndexes(0);
27810     },
27811     
27812
27813     /**
27814      * Function to override to reformat the data that is sent to
27815      * the template for each node.
27816      * DEPRICATED - use the preparedata event handler.
27817      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27818      * a JSON object for an UpdateManager bound view).
27819      */
27820     prepareData : function(data, index, record)
27821     {
27822         this.fireEvent("preparedata", this, data, index, record);
27823         return data;
27824     },
27825
27826     onUpdate : function(ds, record){
27827         // Roo.log('on update');   
27828         this.clearSelections();
27829         var index = this.store.indexOf(record);
27830         var n = this.nodes[index];
27831         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27832         n.parentNode.removeChild(n);
27833         this.updateIndexes(index, index);
27834     },
27835
27836     
27837     
27838 // --------- FIXME     
27839     onAdd : function(ds, records, index)
27840     {
27841         //Roo.log(['on Add', ds, records, index] );        
27842         this.clearSelections();
27843         if(this.nodes.length == 0){
27844             this.refresh();
27845             return;
27846         }
27847         var n = this.nodes[index];
27848         for(var i = 0, len = records.length; i < len; i++){
27849             var d = this.prepareData(records[i].data, i, records[i]);
27850             if(n){
27851                 this.tpl.insertBefore(n, d);
27852             }else{
27853                 
27854                 this.tpl.append(this.el, d);
27855             }
27856         }
27857         this.updateIndexes(index);
27858     },
27859
27860     onRemove : function(ds, record, index){
27861        // Roo.log('onRemove');
27862         this.clearSelections();
27863         var el = this.dataName  ?
27864             this.el.child('.roo-tpl-' + this.dataName) :
27865             this.el; 
27866         
27867         el.dom.removeChild(this.nodes[index]);
27868         this.updateIndexes(index);
27869     },
27870
27871     /**
27872      * Refresh an individual node.
27873      * @param {Number} index
27874      */
27875     refreshNode : function(index){
27876         this.onUpdate(this.store, this.store.getAt(index));
27877     },
27878
27879     updateIndexes : function(startIndex, endIndex){
27880         var ns = this.nodes;
27881         startIndex = startIndex || 0;
27882         endIndex = endIndex || ns.length - 1;
27883         for(var i = startIndex; i <= endIndex; i++){
27884             ns[i].nodeIndex = i;
27885         }
27886     },
27887
27888     /**
27889      * Changes the data store this view uses and refresh the view.
27890      * @param {Store} store
27891      */
27892     setStore : function(store, initial){
27893         if(!initial && this.store){
27894             this.store.un("datachanged", this.refresh);
27895             this.store.un("add", this.onAdd);
27896             this.store.un("remove", this.onRemove);
27897             this.store.un("update", this.onUpdate);
27898             this.store.un("clear", this.refresh);
27899             this.store.un("beforeload", this.onBeforeLoad);
27900             this.store.un("load", this.onLoad);
27901             this.store.un("loadexception", this.onLoad);
27902         }
27903         if(store){
27904           
27905             store.on("datachanged", this.refresh, this);
27906             store.on("add", this.onAdd, this);
27907             store.on("remove", this.onRemove, this);
27908             store.on("update", this.onUpdate, this);
27909             store.on("clear", this.refresh, this);
27910             store.on("beforeload", this.onBeforeLoad, this);
27911             store.on("load", this.onLoad, this);
27912             store.on("loadexception", this.onLoad, this);
27913         }
27914         
27915         if(store){
27916             this.refresh();
27917         }
27918     },
27919     /**
27920      * onbeforeLoad - masks the loading area.
27921      *
27922      */
27923     onBeforeLoad : function(store,opts)
27924     {
27925          //Roo.log('onBeforeLoad');   
27926         if (!opts.add) {
27927             this.el.update("");
27928         }
27929         this.el.mask(this.mask ? this.mask : "Loading" ); 
27930     },
27931     onLoad : function ()
27932     {
27933         this.el.unmask();
27934     },
27935     
27936
27937     /**
27938      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27939      * @param {HTMLElement} node
27940      * @return {HTMLElement} The template node
27941      */
27942     findItemFromChild : function(node){
27943         var el = this.dataName  ?
27944             this.el.child('.roo-tpl-' + this.dataName,true) :
27945             this.el.dom; 
27946         
27947         if(!node || node.parentNode == el){
27948                     return node;
27949             }
27950             var p = node.parentNode;
27951             while(p && p != el){
27952             if(p.parentNode == el){
27953                 return p;
27954             }
27955             p = p.parentNode;
27956         }
27957             return null;
27958     },
27959
27960     /** @ignore */
27961     onClick : function(e){
27962         var item = this.findItemFromChild(e.getTarget());
27963         if(item){
27964             var index = this.indexOf(item);
27965             if(this.onItemClick(item, index, e) !== false){
27966                 this.fireEvent("click", this, index, item, e);
27967             }
27968         }else{
27969             this.clearSelections();
27970         }
27971     },
27972
27973     /** @ignore */
27974     onContextMenu : function(e){
27975         var item = this.findItemFromChild(e.getTarget());
27976         if(item){
27977             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27978         }
27979     },
27980
27981     /** @ignore */
27982     onDblClick : function(e){
27983         var item = this.findItemFromChild(e.getTarget());
27984         if(item){
27985             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27986         }
27987     },
27988
27989     onItemClick : function(item, index, e)
27990     {
27991         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27992             return false;
27993         }
27994         if (this.toggleSelect) {
27995             var m = this.isSelected(item) ? 'unselect' : 'select';
27996             //Roo.log(m);
27997             var _t = this;
27998             _t[m](item, true, false);
27999             return true;
28000         }
28001         if(this.multiSelect || this.singleSelect){
28002             if(this.multiSelect && e.shiftKey && this.lastSelection){
28003                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28004             }else{
28005                 this.select(item, this.multiSelect && e.ctrlKey);
28006                 this.lastSelection = item;
28007             }
28008             
28009             if(!this.tickable){
28010                 e.preventDefault();
28011             }
28012             
28013         }
28014         return true;
28015     },
28016
28017     /**
28018      * Get the number of selected nodes.
28019      * @return {Number}
28020      */
28021     getSelectionCount : function(){
28022         return this.selections.length;
28023     },
28024
28025     /**
28026      * Get the currently selected nodes.
28027      * @return {Array} An array of HTMLElements
28028      */
28029     getSelectedNodes : function(){
28030         return this.selections;
28031     },
28032
28033     /**
28034      * Get the indexes of the selected nodes.
28035      * @return {Array}
28036      */
28037     getSelectedIndexes : function(){
28038         var indexes = [], s = this.selections;
28039         for(var i = 0, len = s.length; i < len; i++){
28040             indexes.push(s[i].nodeIndex);
28041         }
28042         return indexes;
28043     },
28044
28045     /**
28046      * Clear all selections
28047      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28048      */
28049     clearSelections : function(suppressEvent){
28050         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28051             this.cmp.elements = this.selections;
28052             this.cmp.removeClass(this.selectedClass);
28053             this.selections = [];
28054             if(!suppressEvent){
28055                 this.fireEvent("selectionchange", this, this.selections);
28056             }
28057         }
28058     },
28059
28060     /**
28061      * Returns true if the passed node is selected
28062      * @param {HTMLElement/Number} node The node or node index
28063      * @return {Boolean}
28064      */
28065     isSelected : function(node){
28066         var s = this.selections;
28067         if(s.length < 1){
28068             return false;
28069         }
28070         node = this.getNode(node);
28071         return s.indexOf(node) !== -1;
28072     },
28073
28074     /**
28075      * Selects nodes.
28076      * @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
28077      * @param {Boolean} keepExisting (optional) true to keep existing selections
28078      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28079      */
28080     select : function(nodeInfo, keepExisting, suppressEvent){
28081         if(nodeInfo instanceof Array){
28082             if(!keepExisting){
28083                 this.clearSelections(true);
28084             }
28085             for(var i = 0, len = nodeInfo.length; i < len; i++){
28086                 this.select(nodeInfo[i], true, true);
28087             }
28088             return;
28089         } 
28090         var node = this.getNode(nodeInfo);
28091         if(!node || this.isSelected(node)){
28092             return; // already selected.
28093         }
28094         if(!keepExisting){
28095             this.clearSelections(true);
28096         }
28097         
28098         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28099             Roo.fly(node).addClass(this.selectedClass);
28100             this.selections.push(node);
28101             if(!suppressEvent){
28102                 this.fireEvent("selectionchange", this, this.selections);
28103             }
28104         }
28105         
28106         
28107     },
28108       /**
28109      * Unselects nodes.
28110      * @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
28111      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28112      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28113      */
28114     unselect : function(nodeInfo, keepExisting, suppressEvent)
28115     {
28116         if(nodeInfo instanceof Array){
28117             Roo.each(this.selections, function(s) {
28118                 this.unselect(s, nodeInfo);
28119             }, this);
28120             return;
28121         }
28122         var node = this.getNode(nodeInfo);
28123         if(!node || !this.isSelected(node)){
28124             //Roo.log("not selected");
28125             return; // not selected.
28126         }
28127         // fireevent???
28128         var ns = [];
28129         Roo.each(this.selections, function(s) {
28130             if (s == node ) {
28131                 Roo.fly(node).removeClass(this.selectedClass);
28132
28133                 return;
28134             }
28135             ns.push(s);
28136         },this);
28137         
28138         this.selections= ns;
28139         this.fireEvent("selectionchange", this, this.selections);
28140     },
28141
28142     /**
28143      * Gets a template node.
28144      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28145      * @return {HTMLElement} The node or null if it wasn't found
28146      */
28147     getNode : function(nodeInfo){
28148         if(typeof nodeInfo == "string"){
28149             return document.getElementById(nodeInfo);
28150         }else if(typeof nodeInfo == "number"){
28151             return this.nodes[nodeInfo];
28152         }
28153         return nodeInfo;
28154     },
28155
28156     /**
28157      * Gets a range template nodes.
28158      * @param {Number} startIndex
28159      * @param {Number} endIndex
28160      * @return {Array} An array of nodes
28161      */
28162     getNodes : function(start, end){
28163         var ns = this.nodes;
28164         start = start || 0;
28165         end = typeof end == "undefined" ? ns.length - 1 : end;
28166         var nodes = [];
28167         if(start <= end){
28168             for(var i = start; i <= end; i++){
28169                 nodes.push(ns[i]);
28170             }
28171         } else{
28172             for(var i = start; i >= end; i--){
28173                 nodes.push(ns[i]);
28174             }
28175         }
28176         return nodes;
28177     },
28178
28179     /**
28180      * Finds the index of the passed node
28181      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28182      * @return {Number} The index of the node or -1
28183      */
28184     indexOf : function(node){
28185         node = this.getNode(node);
28186         if(typeof node.nodeIndex == "number"){
28187             return node.nodeIndex;
28188         }
28189         var ns = this.nodes;
28190         for(var i = 0, len = ns.length; i < len; i++){
28191             if(ns[i] == node){
28192                 return i;
28193             }
28194         }
28195         return -1;
28196     }
28197 });
28198 /*
28199  * Based on:
28200  * Ext JS Library 1.1.1
28201  * Copyright(c) 2006-2007, Ext JS, LLC.
28202  *
28203  * Originally Released Under LGPL - original licence link has changed is not relivant.
28204  *
28205  * Fork - LGPL
28206  * <script type="text/javascript">
28207  */
28208
28209 /**
28210  * @class Roo.JsonView
28211  * @extends Roo.View
28212  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28213 <pre><code>
28214 var view = new Roo.JsonView({
28215     container: "my-element",
28216     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28217     multiSelect: true, 
28218     jsonRoot: "data" 
28219 });
28220
28221 // listen for node click?
28222 view.on("click", function(vw, index, node, e){
28223     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28224 });
28225
28226 // direct load of JSON data
28227 view.load("foobar.php");
28228
28229 // Example from my blog list
28230 var tpl = new Roo.Template(
28231     '&lt;div class="entry"&gt;' +
28232     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28233     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28234     "&lt;/div&gt;&lt;hr /&gt;"
28235 );
28236
28237 var moreView = new Roo.JsonView({
28238     container :  "entry-list", 
28239     template : tpl,
28240     jsonRoot: "posts"
28241 });
28242 moreView.on("beforerender", this.sortEntries, this);
28243 moreView.load({
28244     url: "/blog/get-posts.php",
28245     params: "allposts=true",
28246     text: "Loading Blog Entries..."
28247 });
28248 </code></pre>
28249
28250 * Note: old code is supported with arguments : (container, template, config)
28251
28252
28253  * @constructor
28254  * Create a new JsonView
28255  * 
28256  * @param {Object} config The config object
28257  * 
28258  */
28259 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28260     
28261     
28262     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28263
28264     var um = this.el.getUpdateManager();
28265     um.setRenderer(this);
28266     um.on("update", this.onLoad, this);
28267     um.on("failure", this.onLoadException, this);
28268
28269     /**
28270      * @event beforerender
28271      * Fires before rendering of the downloaded JSON data.
28272      * @param {Roo.JsonView} this
28273      * @param {Object} data The JSON data loaded
28274      */
28275     /**
28276      * @event load
28277      * Fires when data is loaded.
28278      * @param {Roo.JsonView} this
28279      * @param {Object} data The JSON data loaded
28280      * @param {Object} response The raw Connect response object
28281      */
28282     /**
28283      * @event loadexception
28284      * Fires when loading fails.
28285      * @param {Roo.JsonView} this
28286      * @param {Object} response The raw Connect response object
28287      */
28288     this.addEvents({
28289         'beforerender' : true,
28290         'load' : true,
28291         'loadexception' : true
28292     });
28293 };
28294 Roo.extend(Roo.JsonView, Roo.View, {
28295     /**
28296      * @type {String} The root property in the loaded JSON object that contains the data
28297      */
28298     jsonRoot : "",
28299
28300     /**
28301      * Refreshes the view.
28302      */
28303     refresh : function(){
28304         this.clearSelections();
28305         this.el.update("");
28306         var html = [];
28307         var o = this.jsonData;
28308         if(o && o.length > 0){
28309             for(var i = 0, len = o.length; i < len; i++){
28310                 var data = this.prepareData(o[i], i, o);
28311                 html[html.length] = this.tpl.apply(data);
28312             }
28313         }else{
28314             html.push(this.emptyText);
28315         }
28316         this.el.update(html.join(""));
28317         this.nodes = this.el.dom.childNodes;
28318         this.updateIndexes(0);
28319     },
28320
28321     /**
28322      * 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.
28323      * @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:
28324      <pre><code>
28325      view.load({
28326          url: "your-url.php",
28327          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28328          callback: yourFunction,
28329          scope: yourObject, //(optional scope)
28330          discardUrl: false,
28331          nocache: false,
28332          text: "Loading...",
28333          timeout: 30,
28334          scripts: false
28335      });
28336      </code></pre>
28337      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28338      * 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.
28339      * @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}
28340      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28341      * @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.
28342      */
28343     load : function(){
28344         var um = this.el.getUpdateManager();
28345         um.update.apply(um, arguments);
28346     },
28347
28348     // note - render is a standard framework call...
28349     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28350     render : function(el, response){
28351         
28352         this.clearSelections();
28353         this.el.update("");
28354         var o;
28355         try{
28356             if (response != '') {
28357                 o = Roo.util.JSON.decode(response.responseText);
28358                 if(this.jsonRoot){
28359                     
28360                     o = o[this.jsonRoot];
28361                 }
28362             }
28363         } catch(e){
28364         }
28365         /**
28366          * The current JSON data or null
28367          */
28368         this.jsonData = o;
28369         this.beforeRender();
28370         this.refresh();
28371     },
28372
28373 /**
28374  * Get the number of records in the current JSON dataset
28375  * @return {Number}
28376  */
28377     getCount : function(){
28378         return this.jsonData ? this.jsonData.length : 0;
28379     },
28380
28381 /**
28382  * Returns the JSON object for the specified node(s)
28383  * @param {HTMLElement/Array} node The node or an array of nodes
28384  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28385  * you get the JSON object for the node
28386  */
28387     getNodeData : function(node){
28388         if(node instanceof Array){
28389             var data = [];
28390             for(var i = 0, len = node.length; i < len; i++){
28391                 data.push(this.getNodeData(node[i]));
28392             }
28393             return data;
28394         }
28395         return this.jsonData[this.indexOf(node)] || null;
28396     },
28397
28398     beforeRender : function(){
28399         this.snapshot = this.jsonData;
28400         if(this.sortInfo){
28401             this.sort.apply(this, this.sortInfo);
28402         }
28403         this.fireEvent("beforerender", this, this.jsonData);
28404     },
28405
28406     onLoad : function(el, o){
28407         this.fireEvent("load", this, this.jsonData, o);
28408     },
28409
28410     onLoadException : function(el, o){
28411         this.fireEvent("loadexception", this, o);
28412     },
28413
28414 /**
28415  * Filter the data by a specific property.
28416  * @param {String} property A property on your JSON objects
28417  * @param {String/RegExp} value Either string that the property values
28418  * should start with, or a RegExp to test against the property
28419  */
28420     filter : function(property, value){
28421         if(this.jsonData){
28422             var data = [];
28423             var ss = this.snapshot;
28424             if(typeof value == "string"){
28425                 var vlen = value.length;
28426                 if(vlen == 0){
28427                     this.clearFilter();
28428                     return;
28429                 }
28430                 value = value.toLowerCase();
28431                 for(var i = 0, len = ss.length; i < len; i++){
28432                     var o = ss[i];
28433                     if(o[property].substr(0, vlen).toLowerCase() == value){
28434                         data.push(o);
28435                     }
28436                 }
28437             } else if(value.exec){ // regex?
28438                 for(var i = 0, len = ss.length; i < len; i++){
28439                     var o = ss[i];
28440                     if(value.test(o[property])){
28441                         data.push(o);
28442                     }
28443                 }
28444             } else{
28445                 return;
28446             }
28447             this.jsonData = data;
28448             this.refresh();
28449         }
28450     },
28451
28452 /**
28453  * Filter by a function. The passed function will be called with each
28454  * object in the current dataset. If the function returns true the value is kept,
28455  * otherwise it is filtered.
28456  * @param {Function} fn
28457  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28458  */
28459     filterBy : function(fn, scope){
28460         if(this.jsonData){
28461             var data = [];
28462             var ss = this.snapshot;
28463             for(var i = 0, len = ss.length; i < len; i++){
28464                 var o = ss[i];
28465                 if(fn.call(scope || this, o)){
28466                     data.push(o);
28467                 }
28468             }
28469             this.jsonData = data;
28470             this.refresh();
28471         }
28472     },
28473
28474 /**
28475  * Clears the current filter.
28476  */
28477     clearFilter : function(){
28478         if(this.snapshot && this.jsonData != this.snapshot){
28479             this.jsonData = this.snapshot;
28480             this.refresh();
28481         }
28482     },
28483
28484
28485 /**
28486  * Sorts the data for this view and refreshes it.
28487  * @param {String} property A property on your JSON objects to sort on
28488  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28489  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28490  */
28491     sort : function(property, dir, sortType){
28492         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28493         if(this.jsonData){
28494             var p = property;
28495             var dsc = dir && dir.toLowerCase() == "desc";
28496             var f = function(o1, o2){
28497                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28498                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28499                 ;
28500                 if(v1 < v2){
28501                     return dsc ? +1 : -1;
28502                 } else if(v1 > v2){
28503                     return dsc ? -1 : +1;
28504                 } else{
28505                     return 0;
28506                 }
28507             };
28508             this.jsonData.sort(f);
28509             this.refresh();
28510             if(this.jsonData != this.snapshot){
28511                 this.snapshot.sort(f);
28512             }
28513         }
28514     }
28515 });/*
28516  * Based on:
28517  * Ext JS Library 1.1.1
28518  * Copyright(c) 2006-2007, Ext JS, LLC.
28519  *
28520  * Originally Released Under LGPL - original licence link has changed is not relivant.
28521  *
28522  * Fork - LGPL
28523  * <script type="text/javascript">
28524  */
28525  
28526
28527 /**
28528  * @class Roo.ColorPalette
28529  * @extends Roo.Component
28530  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28531  * Here's an example of typical usage:
28532  * <pre><code>
28533 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28534 cp.render('my-div');
28535
28536 cp.on('select', function(palette, selColor){
28537     // do something with selColor
28538 });
28539 </code></pre>
28540  * @constructor
28541  * Create a new ColorPalette
28542  * @param {Object} config The config object
28543  */
28544 Roo.ColorPalette = function(config){
28545     Roo.ColorPalette.superclass.constructor.call(this, config);
28546     this.addEvents({
28547         /**
28548              * @event select
28549              * Fires when a color is selected
28550              * @param {ColorPalette} this
28551              * @param {String} color The 6-digit color hex code (without the # symbol)
28552              */
28553         select: true
28554     });
28555
28556     if(this.handler){
28557         this.on("select", this.handler, this.scope, true);
28558     }
28559 };
28560 Roo.extend(Roo.ColorPalette, Roo.Component, {
28561     /**
28562      * @cfg {String} itemCls
28563      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28564      */
28565     itemCls : "x-color-palette",
28566     /**
28567      * @cfg {String} value
28568      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28569      * the hex codes are case-sensitive.
28570      */
28571     value : null,
28572     clickEvent:'click',
28573     // private
28574     ctype: "Roo.ColorPalette",
28575
28576     /**
28577      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28578      */
28579     allowReselect : false,
28580
28581     /**
28582      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28583      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28584      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28585      * of colors with the width setting until the box is symmetrical.</p>
28586      * <p>You can override individual colors if needed:</p>
28587      * <pre><code>
28588 var cp = new Roo.ColorPalette();
28589 cp.colors[0] = "FF0000";  // change the first box to red
28590 </code></pre>
28591
28592 Or you can provide a custom array of your own for complete control:
28593 <pre><code>
28594 var cp = new Roo.ColorPalette();
28595 cp.colors = ["000000", "993300", "333300"];
28596 </code></pre>
28597      * @type Array
28598      */
28599     colors : [
28600         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28601         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28602         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28603         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28604         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28605     ],
28606
28607     // private
28608     onRender : function(container, position){
28609         var t = new Roo.MasterTemplate(
28610             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28611         );
28612         var c = this.colors;
28613         for(var i = 0, len = c.length; i < len; i++){
28614             t.add([c[i]]);
28615         }
28616         var el = document.createElement("div");
28617         el.className = this.itemCls;
28618         t.overwrite(el);
28619         container.dom.insertBefore(el, position);
28620         this.el = Roo.get(el);
28621         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28622         if(this.clickEvent != 'click'){
28623             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28624         }
28625     },
28626
28627     // private
28628     afterRender : function(){
28629         Roo.ColorPalette.superclass.afterRender.call(this);
28630         if(this.value){
28631             var s = this.value;
28632             this.value = null;
28633             this.select(s);
28634         }
28635     },
28636
28637     // private
28638     handleClick : function(e, t){
28639         e.preventDefault();
28640         if(!this.disabled){
28641             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28642             this.select(c.toUpperCase());
28643         }
28644     },
28645
28646     /**
28647      * Selects the specified color in the palette (fires the select event)
28648      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28649      */
28650     select : function(color){
28651         color = color.replace("#", "");
28652         if(color != this.value || this.allowReselect){
28653             var el = this.el;
28654             if(this.value){
28655                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28656             }
28657             el.child("a.color-"+color).addClass("x-color-palette-sel");
28658             this.value = color;
28659             this.fireEvent("select", this, color);
28660         }
28661     }
28662 });/*
28663  * Based on:
28664  * Ext JS Library 1.1.1
28665  * Copyright(c) 2006-2007, Ext JS, LLC.
28666  *
28667  * Originally Released Under LGPL - original licence link has changed is not relivant.
28668  *
28669  * Fork - LGPL
28670  * <script type="text/javascript">
28671  */
28672  
28673 /**
28674  * @class Roo.DatePicker
28675  * @extends Roo.Component
28676  * Simple date picker class.
28677  * @constructor
28678  * Create a new DatePicker
28679  * @param {Object} config The config object
28680  */
28681 Roo.DatePicker = function(config){
28682     Roo.DatePicker.superclass.constructor.call(this, config);
28683
28684     this.value = config && config.value ?
28685                  config.value.clearTime() : new Date().clearTime();
28686
28687     this.addEvents({
28688         /**
28689              * @event select
28690              * Fires when a date is selected
28691              * @param {DatePicker} this
28692              * @param {Date} date The selected date
28693              */
28694         'select': true,
28695         /**
28696              * @event monthchange
28697              * Fires when the displayed month changes 
28698              * @param {DatePicker} this
28699              * @param {Date} date The selected month
28700              */
28701         'monthchange': true
28702     });
28703
28704     if(this.handler){
28705         this.on("select", this.handler,  this.scope || this);
28706     }
28707     // build the disabledDatesRE
28708     if(!this.disabledDatesRE && this.disabledDates){
28709         var dd = this.disabledDates;
28710         var re = "(?:";
28711         for(var i = 0; i < dd.length; i++){
28712             re += dd[i];
28713             if(i != dd.length-1) {
28714                 re += "|";
28715             }
28716         }
28717         this.disabledDatesRE = new RegExp(re + ")");
28718     }
28719 };
28720
28721 Roo.extend(Roo.DatePicker, Roo.Component, {
28722     /**
28723      * @cfg {String} todayText
28724      * The text to display on the button that selects the current date (defaults to "Today")
28725      */
28726     todayText : "Today",
28727     /**
28728      * @cfg {String} okText
28729      * The text to display on the ok button
28730      */
28731     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28732     /**
28733      * @cfg {String} cancelText
28734      * The text to display on the cancel button
28735      */
28736     cancelText : "Cancel",
28737     /**
28738      * @cfg {String} todayTip
28739      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28740      */
28741     todayTip : "{0} (Spacebar)",
28742     /**
28743      * @cfg {Date} minDate
28744      * Minimum allowable date (JavaScript date object, defaults to null)
28745      */
28746     minDate : null,
28747     /**
28748      * @cfg {Date} maxDate
28749      * Maximum allowable date (JavaScript date object, defaults to null)
28750      */
28751     maxDate : null,
28752     /**
28753      * @cfg {String} minText
28754      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28755      */
28756     minText : "This date is before the minimum date",
28757     /**
28758      * @cfg {String} maxText
28759      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28760      */
28761     maxText : "This date is after the maximum date",
28762     /**
28763      * @cfg {String} format
28764      * The default date format string which can be overriden for localization support.  The format must be
28765      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28766      */
28767     format : "m/d/y",
28768     /**
28769      * @cfg {Array} disabledDays
28770      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28771      */
28772     disabledDays : null,
28773     /**
28774      * @cfg {String} disabledDaysText
28775      * The tooltip to display when the date falls on a disabled day (defaults to "")
28776      */
28777     disabledDaysText : "",
28778     /**
28779      * @cfg {RegExp} disabledDatesRE
28780      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28781      */
28782     disabledDatesRE : null,
28783     /**
28784      * @cfg {String} disabledDatesText
28785      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28786      */
28787     disabledDatesText : "",
28788     /**
28789      * @cfg {Boolean} constrainToViewport
28790      * True to constrain the date picker to the viewport (defaults to true)
28791      */
28792     constrainToViewport : true,
28793     /**
28794      * @cfg {Array} monthNames
28795      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28796      */
28797     monthNames : Date.monthNames,
28798     /**
28799      * @cfg {Array} dayNames
28800      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28801      */
28802     dayNames : Date.dayNames,
28803     /**
28804      * @cfg {String} nextText
28805      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28806      */
28807     nextText: 'Next Month (Control+Right)',
28808     /**
28809      * @cfg {String} prevText
28810      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28811      */
28812     prevText: 'Previous Month (Control+Left)',
28813     /**
28814      * @cfg {String} monthYearText
28815      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28816      */
28817     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28818     /**
28819      * @cfg {Number} startDay
28820      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28821      */
28822     startDay : 0,
28823     /**
28824      * @cfg {Bool} showClear
28825      * Show a clear button (usefull for date form elements that can be blank.)
28826      */
28827     
28828     showClear: false,
28829     
28830     /**
28831      * Sets the value of the date field
28832      * @param {Date} value The date to set
28833      */
28834     setValue : function(value){
28835         var old = this.value;
28836         
28837         if (typeof(value) == 'string') {
28838          
28839             value = Date.parseDate(value, this.format);
28840         }
28841         if (!value) {
28842             value = new Date();
28843         }
28844         
28845         this.value = value.clearTime(true);
28846         if(this.el){
28847             this.update(this.value);
28848         }
28849     },
28850
28851     /**
28852      * Gets the current selected value of the date field
28853      * @return {Date} The selected date
28854      */
28855     getValue : function(){
28856         return this.value;
28857     },
28858
28859     // private
28860     focus : function(){
28861         if(this.el){
28862             this.update(this.activeDate);
28863         }
28864     },
28865
28866     // privateval
28867     onRender : function(container, position){
28868         
28869         var m = [
28870              '<table cellspacing="0">',
28871                 '<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>',
28872                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28873         var dn = this.dayNames;
28874         for(var i = 0; i < 7; i++){
28875             var d = this.startDay+i;
28876             if(d > 6){
28877                 d = d-7;
28878             }
28879             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28880         }
28881         m[m.length] = "</tr></thead><tbody><tr>";
28882         for(var i = 0; i < 42; i++) {
28883             if(i % 7 == 0 && i != 0){
28884                 m[m.length] = "</tr><tr>";
28885             }
28886             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28887         }
28888         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28889             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28890
28891         var el = document.createElement("div");
28892         el.className = "x-date-picker";
28893         el.innerHTML = m.join("");
28894
28895         container.dom.insertBefore(el, position);
28896
28897         this.el = Roo.get(el);
28898         this.eventEl = Roo.get(el.firstChild);
28899
28900         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28901             handler: this.showPrevMonth,
28902             scope: this,
28903             preventDefault:true,
28904             stopDefault:true
28905         });
28906
28907         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28908             handler: this.showNextMonth,
28909             scope: this,
28910             preventDefault:true,
28911             stopDefault:true
28912         });
28913
28914         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28915
28916         this.monthPicker = this.el.down('div.x-date-mp');
28917         this.monthPicker.enableDisplayMode('block');
28918         
28919         var kn = new Roo.KeyNav(this.eventEl, {
28920             "left" : function(e){
28921                 e.ctrlKey ?
28922                     this.showPrevMonth() :
28923                     this.update(this.activeDate.add("d", -1));
28924             },
28925
28926             "right" : function(e){
28927                 e.ctrlKey ?
28928                     this.showNextMonth() :
28929                     this.update(this.activeDate.add("d", 1));
28930             },
28931
28932             "up" : function(e){
28933                 e.ctrlKey ?
28934                     this.showNextYear() :
28935                     this.update(this.activeDate.add("d", -7));
28936             },
28937
28938             "down" : function(e){
28939                 e.ctrlKey ?
28940                     this.showPrevYear() :
28941                     this.update(this.activeDate.add("d", 7));
28942             },
28943
28944             "pageUp" : function(e){
28945                 this.showNextMonth();
28946             },
28947
28948             "pageDown" : function(e){
28949                 this.showPrevMonth();
28950             },
28951
28952             "enter" : function(e){
28953                 e.stopPropagation();
28954                 return true;
28955             },
28956
28957             scope : this
28958         });
28959
28960         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28961
28962         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28963
28964         this.el.unselectable();
28965         
28966         this.cells = this.el.select("table.x-date-inner tbody td");
28967         this.textNodes = this.el.query("table.x-date-inner tbody span");
28968
28969         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28970             text: "&#160;",
28971             tooltip: this.monthYearText
28972         });
28973
28974         this.mbtn.on('click', this.showMonthPicker, this);
28975         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28976
28977
28978         var today = (new Date()).dateFormat(this.format);
28979         
28980         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28981         if (this.showClear) {
28982             baseTb.add( new Roo.Toolbar.Fill());
28983         }
28984         baseTb.add({
28985             text: String.format(this.todayText, today),
28986             tooltip: String.format(this.todayTip, today),
28987             handler: this.selectToday,
28988             scope: this
28989         });
28990         
28991         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28992             
28993         //});
28994         if (this.showClear) {
28995             
28996             baseTb.add( new Roo.Toolbar.Fill());
28997             baseTb.add({
28998                 text: '&#160;',
28999                 cls: 'x-btn-icon x-btn-clear',
29000                 handler: function() {
29001                     //this.value = '';
29002                     this.fireEvent("select", this, '');
29003                 },
29004                 scope: this
29005             });
29006         }
29007         
29008         
29009         if(Roo.isIE){
29010             this.el.repaint();
29011         }
29012         this.update(this.value);
29013     },
29014
29015     createMonthPicker : function(){
29016         if(!this.monthPicker.dom.firstChild){
29017             var buf = ['<table border="0" cellspacing="0">'];
29018             for(var i = 0; i < 6; i++){
29019                 buf.push(
29020                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29021                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29022                     i == 0 ?
29023                     '<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>' :
29024                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29025                 );
29026             }
29027             buf.push(
29028                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29029                     this.okText,
29030                     '</button><button type="button" class="x-date-mp-cancel">',
29031                     this.cancelText,
29032                     '</button></td></tr>',
29033                 '</table>'
29034             );
29035             this.monthPicker.update(buf.join(''));
29036             this.monthPicker.on('click', this.onMonthClick, this);
29037             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29038
29039             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29040             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29041
29042             this.mpMonths.each(function(m, a, i){
29043                 i += 1;
29044                 if((i%2) == 0){
29045                     m.dom.xmonth = 5 + Math.round(i * .5);
29046                 }else{
29047                     m.dom.xmonth = Math.round((i-1) * .5);
29048                 }
29049             });
29050         }
29051     },
29052
29053     showMonthPicker : function(){
29054         this.createMonthPicker();
29055         var size = this.el.getSize();
29056         this.monthPicker.setSize(size);
29057         this.monthPicker.child('table').setSize(size);
29058
29059         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29060         this.updateMPMonth(this.mpSelMonth);
29061         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29062         this.updateMPYear(this.mpSelYear);
29063
29064         this.monthPicker.slideIn('t', {duration:.2});
29065     },
29066
29067     updateMPYear : function(y){
29068         this.mpyear = y;
29069         var ys = this.mpYears.elements;
29070         for(var i = 1; i <= 10; i++){
29071             var td = ys[i-1], y2;
29072             if((i%2) == 0){
29073                 y2 = y + Math.round(i * .5);
29074                 td.firstChild.innerHTML = y2;
29075                 td.xyear = y2;
29076             }else{
29077                 y2 = y - (5-Math.round(i * .5));
29078                 td.firstChild.innerHTML = y2;
29079                 td.xyear = y2;
29080             }
29081             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29082         }
29083     },
29084
29085     updateMPMonth : function(sm){
29086         this.mpMonths.each(function(m, a, i){
29087             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29088         });
29089     },
29090
29091     selectMPMonth: function(m){
29092         
29093     },
29094
29095     onMonthClick : function(e, t){
29096         e.stopEvent();
29097         var el = new Roo.Element(t), pn;
29098         if(el.is('button.x-date-mp-cancel')){
29099             this.hideMonthPicker();
29100         }
29101         else if(el.is('button.x-date-mp-ok')){
29102             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29103             this.hideMonthPicker();
29104         }
29105         else if(pn = el.up('td.x-date-mp-month', 2)){
29106             this.mpMonths.removeClass('x-date-mp-sel');
29107             pn.addClass('x-date-mp-sel');
29108             this.mpSelMonth = pn.dom.xmonth;
29109         }
29110         else if(pn = el.up('td.x-date-mp-year', 2)){
29111             this.mpYears.removeClass('x-date-mp-sel');
29112             pn.addClass('x-date-mp-sel');
29113             this.mpSelYear = pn.dom.xyear;
29114         }
29115         else if(el.is('a.x-date-mp-prev')){
29116             this.updateMPYear(this.mpyear-10);
29117         }
29118         else if(el.is('a.x-date-mp-next')){
29119             this.updateMPYear(this.mpyear+10);
29120         }
29121     },
29122
29123     onMonthDblClick : function(e, t){
29124         e.stopEvent();
29125         var el = new Roo.Element(t), pn;
29126         if(pn = el.up('td.x-date-mp-month', 2)){
29127             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29128             this.hideMonthPicker();
29129         }
29130         else if(pn = el.up('td.x-date-mp-year', 2)){
29131             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29132             this.hideMonthPicker();
29133         }
29134     },
29135
29136     hideMonthPicker : function(disableAnim){
29137         if(this.monthPicker){
29138             if(disableAnim === true){
29139                 this.monthPicker.hide();
29140             }else{
29141                 this.monthPicker.slideOut('t', {duration:.2});
29142             }
29143         }
29144     },
29145
29146     // private
29147     showPrevMonth : function(e){
29148         this.update(this.activeDate.add("mo", -1));
29149     },
29150
29151     // private
29152     showNextMonth : function(e){
29153         this.update(this.activeDate.add("mo", 1));
29154     },
29155
29156     // private
29157     showPrevYear : function(){
29158         this.update(this.activeDate.add("y", -1));
29159     },
29160
29161     // private
29162     showNextYear : function(){
29163         this.update(this.activeDate.add("y", 1));
29164     },
29165
29166     // private
29167     handleMouseWheel : function(e){
29168         var delta = e.getWheelDelta();
29169         if(delta > 0){
29170             this.showPrevMonth();
29171             e.stopEvent();
29172         } else if(delta < 0){
29173             this.showNextMonth();
29174             e.stopEvent();
29175         }
29176     },
29177
29178     // private
29179     handleDateClick : function(e, t){
29180         e.stopEvent();
29181         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29182             this.setValue(new Date(t.dateValue));
29183             this.fireEvent("select", this, this.value);
29184         }
29185     },
29186
29187     // private
29188     selectToday : function(){
29189         this.setValue(new Date().clearTime());
29190         this.fireEvent("select", this, this.value);
29191     },
29192
29193     // private
29194     update : function(date)
29195     {
29196         var vd = this.activeDate;
29197         this.activeDate = date;
29198         if(vd && this.el){
29199             var t = date.getTime();
29200             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29201                 this.cells.removeClass("x-date-selected");
29202                 this.cells.each(function(c){
29203                    if(c.dom.firstChild.dateValue == t){
29204                        c.addClass("x-date-selected");
29205                        setTimeout(function(){
29206                             try{c.dom.firstChild.focus();}catch(e){}
29207                        }, 50);
29208                        return false;
29209                    }
29210                 });
29211                 return;
29212             }
29213         }
29214         
29215         var days = date.getDaysInMonth();
29216         var firstOfMonth = date.getFirstDateOfMonth();
29217         var startingPos = firstOfMonth.getDay()-this.startDay;
29218
29219         if(startingPos <= this.startDay){
29220             startingPos += 7;
29221         }
29222
29223         var pm = date.add("mo", -1);
29224         var prevStart = pm.getDaysInMonth()-startingPos;
29225
29226         var cells = this.cells.elements;
29227         var textEls = this.textNodes;
29228         days += startingPos;
29229
29230         // convert everything to numbers so it's fast
29231         var day = 86400000;
29232         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29233         var today = new Date().clearTime().getTime();
29234         var sel = date.clearTime().getTime();
29235         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29236         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29237         var ddMatch = this.disabledDatesRE;
29238         var ddText = this.disabledDatesText;
29239         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29240         var ddaysText = this.disabledDaysText;
29241         var format = this.format;
29242
29243         var setCellClass = function(cal, cell){
29244             cell.title = "";
29245             var t = d.getTime();
29246             cell.firstChild.dateValue = t;
29247             if(t == today){
29248                 cell.className += " x-date-today";
29249                 cell.title = cal.todayText;
29250             }
29251             if(t == sel){
29252                 cell.className += " x-date-selected";
29253                 setTimeout(function(){
29254                     try{cell.firstChild.focus();}catch(e){}
29255                 }, 50);
29256             }
29257             // disabling
29258             if(t < min) {
29259                 cell.className = " x-date-disabled";
29260                 cell.title = cal.minText;
29261                 return;
29262             }
29263             if(t > max) {
29264                 cell.className = " x-date-disabled";
29265                 cell.title = cal.maxText;
29266                 return;
29267             }
29268             if(ddays){
29269                 if(ddays.indexOf(d.getDay()) != -1){
29270                     cell.title = ddaysText;
29271                     cell.className = " x-date-disabled";
29272                 }
29273             }
29274             if(ddMatch && format){
29275                 var fvalue = d.dateFormat(format);
29276                 if(ddMatch.test(fvalue)){
29277                     cell.title = ddText.replace("%0", fvalue);
29278                     cell.className = " x-date-disabled";
29279                 }
29280             }
29281         };
29282
29283         var i = 0;
29284         for(; i < startingPos; i++) {
29285             textEls[i].innerHTML = (++prevStart);
29286             d.setDate(d.getDate()+1);
29287             cells[i].className = "x-date-prevday";
29288             setCellClass(this, cells[i]);
29289         }
29290         for(; i < days; i++){
29291             intDay = i - startingPos + 1;
29292             textEls[i].innerHTML = (intDay);
29293             d.setDate(d.getDate()+1);
29294             cells[i].className = "x-date-active";
29295             setCellClass(this, cells[i]);
29296         }
29297         var extraDays = 0;
29298         for(; i < 42; i++) {
29299              textEls[i].innerHTML = (++extraDays);
29300              d.setDate(d.getDate()+1);
29301              cells[i].className = "x-date-nextday";
29302              setCellClass(this, cells[i]);
29303         }
29304
29305         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29306         this.fireEvent('monthchange', this, date);
29307         
29308         if(!this.internalRender){
29309             var main = this.el.dom.firstChild;
29310             var w = main.offsetWidth;
29311             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29312             Roo.fly(main).setWidth(w);
29313             this.internalRender = true;
29314             // opera does not respect the auto grow header center column
29315             // then, after it gets a width opera refuses to recalculate
29316             // without a second pass
29317             if(Roo.isOpera && !this.secondPass){
29318                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29319                 this.secondPass = true;
29320                 this.update.defer(10, this, [date]);
29321             }
29322         }
29323         
29324         
29325     }
29326 });        /*
29327  * Based on:
29328  * Ext JS Library 1.1.1
29329  * Copyright(c) 2006-2007, Ext JS, LLC.
29330  *
29331  * Originally Released Under LGPL - original licence link has changed is not relivant.
29332  *
29333  * Fork - LGPL
29334  * <script type="text/javascript">
29335  */
29336 /**
29337  * @class Roo.TabPanel
29338  * @extends Roo.util.Observable
29339  * A lightweight tab container.
29340  * <br><br>
29341  * Usage:
29342  * <pre><code>
29343 // basic tabs 1, built from existing content
29344 var tabs = new Roo.TabPanel("tabs1");
29345 tabs.addTab("script", "View Script");
29346 tabs.addTab("markup", "View Markup");
29347 tabs.activate("script");
29348
29349 // more advanced tabs, built from javascript
29350 var jtabs = new Roo.TabPanel("jtabs");
29351 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29352
29353 // set up the UpdateManager
29354 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29355 var updater = tab2.getUpdateManager();
29356 updater.setDefaultUrl("ajax1.htm");
29357 tab2.on('activate', updater.refresh, updater, true);
29358
29359 // Use setUrl for Ajax loading
29360 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29361 tab3.setUrl("ajax2.htm", null, true);
29362
29363 // Disabled tab
29364 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29365 tab4.disable();
29366
29367 jtabs.activate("jtabs-1");
29368  * </code></pre>
29369  * @constructor
29370  * Create a new TabPanel.
29371  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29372  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29373  */
29374 Roo.TabPanel = function(container, config){
29375     /**
29376     * The container element for this TabPanel.
29377     * @type Roo.Element
29378     */
29379     this.el = Roo.get(container, true);
29380     if(config){
29381         if(typeof config == "boolean"){
29382             this.tabPosition = config ? "bottom" : "top";
29383         }else{
29384             Roo.apply(this, config);
29385         }
29386     }
29387     if(this.tabPosition == "bottom"){
29388         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29389         this.el.addClass("x-tabs-bottom");
29390     }
29391     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29392     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29393     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29394     if(Roo.isIE){
29395         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29396     }
29397     if(this.tabPosition != "bottom"){
29398         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29399          * @type Roo.Element
29400          */
29401         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29402         this.el.addClass("x-tabs-top");
29403     }
29404     this.items = [];
29405
29406     this.bodyEl.setStyle("position", "relative");
29407
29408     this.active = null;
29409     this.activateDelegate = this.activate.createDelegate(this);
29410
29411     this.addEvents({
29412         /**
29413          * @event tabchange
29414          * Fires when the active tab changes
29415          * @param {Roo.TabPanel} this
29416          * @param {Roo.TabPanelItem} activePanel The new active tab
29417          */
29418         "tabchange": true,
29419         /**
29420          * @event beforetabchange
29421          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29422          * @param {Roo.TabPanel} this
29423          * @param {Object} e Set cancel to true on this object to cancel the tab change
29424          * @param {Roo.TabPanelItem} tab The tab being changed to
29425          */
29426         "beforetabchange" : true
29427     });
29428
29429     Roo.EventManager.onWindowResize(this.onResize, this);
29430     this.cpad = this.el.getPadding("lr");
29431     this.hiddenCount = 0;
29432
29433
29434     // toolbar on the tabbar support...
29435     if (this.toolbar) {
29436         var tcfg = this.toolbar;
29437         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29438         this.toolbar = new Roo.Toolbar(tcfg);
29439         if (Roo.isSafari) {
29440             var tbl = tcfg.container.child('table', true);
29441             tbl.setAttribute('width', '100%');
29442         }
29443         
29444     }
29445    
29446
29447
29448     Roo.TabPanel.superclass.constructor.call(this);
29449 };
29450
29451 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29452     /*
29453      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29454      */
29455     tabPosition : "top",
29456     /*
29457      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29458      */
29459     currentTabWidth : 0,
29460     /*
29461      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29462      */
29463     minTabWidth : 40,
29464     /*
29465      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29466      */
29467     maxTabWidth : 250,
29468     /*
29469      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29470      */
29471     preferredTabWidth : 175,
29472     /*
29473      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29474      */
29475     resizeTabs : false,
29476     /*
29477      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29478      */
29479     monitorResize : true,
29480     /*
29481      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29482      */
29483     toolbar : false,
29484
29485     /**
29486      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29487      * @param {String} id The id of the div to use <b>or create</b>
29488      * @param {String} text The text for the tab
29489      * @param {String} content (optional) Content to put in the TabPanelItem body
29490      * @param {Boolean} closable (optional) True to create a close icon on the tab
29491      * @return {Roo.TabPanelItem} The created TabPanelItem
29492      */
29493     addTab : function(id, text, content, closable){
29494         var item = new Roo.TabPanelItem(this, id, text, closable);
29495         this.addTabItem(item);
29496         if(content){
29497             item.setContent(content);
29498         }
29499         return item;
29500     },
29501
29502     /**
29503      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29504      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29505      * @return {Roo.TabPanelItem}
29506      */
29507     getTab : function(id){
29508         return this.items[id];
29509     },
29510
29511     /**
29512      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29513      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29514      */
29515     hideTab : function(id){
29516         var t = this.items[id];
29517         if(!t.isHidden()){
29518            t.setHidden(true);
29519            this.hiddenCount++;
29520            this.autoSizeTabs();
29521         }
29522     },
29523
29524     /**
29525      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29526      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29527      */
29528     unhideTab : function(id){
29529         var t = this.items[id];
29530         if(t.isHidden()){
29531            t.setHidden(false);
29532            this.hiddenCount--;
29533            this.autoSizeTabs();
29534         }
29535     },
29536
29537     /**
29538      * Adds an existing {@link Roo.TabPanelItem}.
29539      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29540      */
29541     addTabItem : function(item){
29542         this.items[item.id] = item;
29543         this.items.push(item);
29544         if(this.resizeTabs){
29545            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29546            this.autoSizeTabs();
29547         }else{
29548             item.autoSize();
29549         }
29550     },
29551
29552     /**
29553      * Removes a {@link Roo.TabPanelItem}.
29554      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29555      */
29556     removeTab : function(id){
29557         var items = this.items;
29558         var tab = items[id];
29559         if(!tab) { return; }
29560         var index = items.indexOf(tab);
29561         if(this.active == tab && items.length > 1){
29562             var newTab = this.getNextAvailable(index);
29563             if(newTab) {
29564                 newTab.activate();
29565             }
29566         }
29567         this.stripEl.dom.removeChild(tab.pnode.dom);
29568         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29569             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29570         }
29571         items.splice(index, 1);
29572         delete this.items[tab.id];
29573         tab.fireEvent("close", tab);
29574         tab.purgeListeners();
29575         this.autoSizeTabs();
29576     },
29577
29578     getNextAvailable : function(start){
29579         var items = this.items;
29580         var index = start;
29581         // look for a next tab that will slide over to
29582         // replace the one being removed
29583         while(index < items.length){
29584             var item = items[++index];
29585             if(item && !item.isHidden()){
29586                 return item;
29587             }
29588         }
29589         // if one isn't found select the previous tab (on the left)
29590         index = start;
29591         while(index >= 0){
29592             var item = items[--index];
29593             if(item && !item.isHidden()){
29594                 return item;
29595             }
29596         }
29597         return null;
29598     },
29599
29600     /**
29601      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29602      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29603      */
29604     disableTab : function(id){
29605         var tab = this.items[id];
29606         if(tab && this.active != tab){
29607             tab.disable();
29608         }
29609     },
29610
29611     /**
29612      * Enables a {@link Roo.TabPanelItem} that is disabled.
29613      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29614      */
29615     enableTab : function(id){
29616         var tab = this.items[id];
29617         tab.enable();
29618     },
29619
29620     /**
29621      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29622      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29623      * @return {Roo.TabPanelItem} The TabPanelItem.
29624      */
29625     activate : function(id){
29626         var tab = this.items[id];
29627         if(!tab){
29628             return null;
29629         }
29630         if(tab == this.active || tab.disabled){
29631             return tab;
29632         }
29633         var e = {};
29634         this.fireEvent("beforetabchange", this, e, tab);
29635         if(e.cancel !== true && !tab.disabled){
29636             if(this.active){
29637                 this.active.hide();
29638             }
29639             this.active = this.items[id];
29640             this.active.show();
29641             this.fireEvent("tabchange", this, this.active);
29642         }
29643         return tab;
29644     },
29645
29646     /**
29647      * Gets the active {@link Roo.TabPanelItem}.
29648      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29649      */
29650     getActiveTab : function(){
29651         return this.active;
29652     },
29653
29654     /**
29655      * Updates the tab body element to fit the height of the container element
29656      * for overflow scrolling
29657      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29658      */
29659     syncHeight : function(targetHeight){
29660         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29661         var bm = this.bodyEl.getMargins();
29662         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29663         this.bodyEl.setHeight(newHeight);
29664         return newHeight;
29665     },
29666
29667     onResize : function(){
29668         if(this.monitorResize){
29669             this.autoSizeTabs();
29670         }
29671     },
29672
29673     /**
29674      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29675      */
29676     beginUpdate : function(){
29677         this.updating = true;
29678     },
29679
29680     /**
29681      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29682      */
29683     endUpdate : function(){
29684         this.updating = false;
29685         this.autoSizeTabs();
29686     },
29687
29688     /**
29689      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29690      */
29691     autoSizeTabs : function(){
29692         var count = this.items.length;
29693         var vcount = count - this.hiddenCount;
29694         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29695             return;
29696         }
29697         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29698         var availWidth = Math.floor(w / vcount);
29699         var b = this.stripBody;
29700         if(b.getWidth() > w){
29701             var tabs = this.items;
29702             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29703             if(availWidth < this.minTabWidth){
29704                 /*if(!this.sleft){    // incomplete scrolling code
29705                     this.createScrollButtons();
29706                 }
29707                 this.showScroll();
29708                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29709             }
29710         }else{
29711             if(this.currentTabWidth < this.preferredTabWidth){
29712                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29713             }
29714         }
29715     },
29716
29717     /**
29718      * Returns the number of tabs in this TabPanel.
29719      * @return {Number}
29720      */
29721      getCount : function(){
29722          return this.items.length;
29723      },
29724
29725     /**
29726      * Resizes all the tabs to the passed width
29727      * @param {Number} The new width
29728      */
29729     setTabWidth : function(width){
29730         this.currentTabWidth = width;
29731         for(var i = 0, len = this.items.length; i < len; i++) {
29732                 if(!this.items[i].isHidden()) {
29733                 this.items[i].setWidth(width);
29734             }
29735         }
29736     },
29737
29738     /**
29739      * Destroys this TabPanel
29740      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29741      */
29742     destroy : function(removeEl){
29743         Roo.EventManager.removeResizeListener(this.onResize, this);
29744         for(var i = 0, len = this.items.length; i < len; i++){
29745             this.items[i].purgeListeners();
29746         }
29747         if(removeEl === true){
29748             this.el.update("");
29749             this.el.remove();
29750         }
29751     }
29752 });
29753
29754 /**
29755  * @class Roo.TabPanelItem
29756  * @extends Roo.util.Observable
29757  * Represents an individual item (tab plus body) in a TabPanel.
29758  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29759  * @param {String} id The id of this TabPanelItem
29760  * @param {String} text The text for the tab of this TabPanelItem
29761  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29762  */
29763 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29764     /**
29765      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29766      * @type Roo.TabPanel
29767      */
29768     this.tabPanel = tabPanel;
29769     /**
29770      * The id for this TabPanelItem
29771      * @type String
29772      */
29773     this.id = id;
29774     /** @private */
29775     this.disabled = false;
29776     /** @private */
29777     this.text = text;
29778     /** @private */
29779     this.loaded = false;
29780     this.closable = closable;
29781
29782     /**
29783      * The body element for this TabPanelItem.
29784      * @type Roo.Element
29785      */
29786     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29787     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29788     this.bodyEl.setStyle("display", "block");
29789     this.bodyEl.setStyle("zoom", "1");
29790     this.hideAction();
29791
29792     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29793     /** @private */
29794     this.el = Roo.get(els.el, true);
29795     this.inner = Roo.get(els.inner, true);
29796     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29797     this.pnode = Roo.get(els.el.parentNode, true);
29798     this.el.on("mousedown", this.onTabMouseDown, this);
29799     this.el.on("click", this.onTabClick, this);
29800     /** @private */
29801     if(closable){
29802         var c = Roo.get(els.close, true);
29803         c.dom.title = this.closeText;
29804         c.addClassOnOver("close-over");
29805         c.on("click", this.closeClick, this);
29806      }
29807
29808     this.addEvents({
29809          /**
29810          * @event activate
29811          * Fires when this tab becomes the active tab.
29812          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29813          * @param {Roo.TabPanelItem} this
29814          */
29815         "activate": true,
29816         /**
29817          * @event beforeclose
29818          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29819          * @param {Roo.TabPanelItem} this
29820          * @param {Object} e Set cancel to true on this object to cancel the close.
29821          */
29822         "beforeclose": true,
29823         /**
29824          * @event close
29825          * Fires when this tab is closed.
29826          * @param {Roo.TabPanelItem} this
29827          */
29828          "close": true,
29829         /**
29830          * @event deactivate
29831          * Fires when this tab is no longer the active tab.
29832          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29833          * @param {Roo.TabPanelItem} this
29834          */
29835          "deactivate" : true
29836     });
29837     this.hidden = false;
29838
29839     Roo.TabPanelItem.superclass.constructor.call(this);
29840 };
29841
29842 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29843     purgeListeners : function(){
29844        Roo.util.Observable.prototype.purgeListeners.call(this);
29845        this.el.removeAllListeners();
29846     },
29847     /**
29848      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29849      */
29850     show : function(){
29851         this.pnode.addClass("on");
29852         this.showAction();
29853         if(Roo.isOpera){
29854             this.tabPanel.stripWrap.repaint();
29855         }
29856         this.fireEvent("activate", this.tabPanel, this);
29857     },
29858
29859     /**
29860      * Returns true if this tab is the active tab.
29861      * @return {Boolean}
29862      */
29863     isActive : function(){
29864         return this.tabPanel.getActiveTab() == this;
29865     },
29866
29867     /**
29868      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29869      */
29870     hide : function(){
29871         this.pnode.removeClass("on");
29872         this.hideAction();
29873         this.fireEvent("deactivate", this.tabPanel, this);
29874     },
29875
29876     hideAction : function(){
29877         this.bodyEl.hide();
29878         this.bodyEl.setStyle("position", "absolute");
29879         this.bodyEl.setLeft("-20000px");
29880         this.bodyEl.setTop("-20000px");
29881     },
29882
29883     showAction : function(){
29884         this.bodyEl.setStyle("position", "relative");
29885         this.bodyEl.setTop("");
29886         this.bodyEl.setLeft("");
29887         this.bodyEl.show();
29888     },
29889
29890     /**
29891      * Set the tooltip for the tab.
29892      * @param {String} tooltip The tab's tooltip
29893      */
29894     setTooltip : function(text){
29895         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29896             this.textEl.dom.qtip = text;
29897             this.textEl.dom.removeAttribute('title');
29898         }else{
29899             this.textEl.dom.title = text;
29900         }
29901     },
29902
29903     onTabClick : function(e){
29904         e.preventDefault();
29905         this.tabPanel.activate(this.id);
29906     },
29907
29908     onTabMouseDown : function(e){
29909         e.preventDefault();
29910         this.tabPanel.activate(this.id);
29911     },
29912
29913     getWidth : function(){
29914         return this.inner.getWidth();
29915     },
29916
29917     setWidth : function(width){
29918         var iwidth = width - this.pnode.getPadding("lr");
29919         this.inner.setWidth(iwidth);
29920         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29921         this.pnode.setWidth(width);
29922     },
29923
29924     /**
29925      * Show or hide the tab
29926      * @param {Boolean} hidden True to hide or false to show.
29927      */
29928     setHidden : function(hidden){
29929         this.hidden = hidden;
29930         this.pnode.setStyle("display", hidden ? "none" : "");
29931     },
29932
29933     /**
29934      * Returns true if this tab is "hidden"
29935      * @return {Boolean}
29936      */
29937     isHidden : function(){
29938         return this.hidden;
29939     },
29940
29941     /**
29942      * Returns the text for this tab
29943      * @return {String}
29944      */
29945     getText : function(){
29946         return this.text;
29947     },
29948
29949     autoSize : function(){
29950         //this.el.beginMeasure();
29951         this.textEl.setWidth(1);
29952         /*
29953          *  #2804 [new] Tabs in Roojs
29954          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29955          */
29956         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29957         //this.el.endMeasure();
29958     },
29959
29960     /**
29961      * Sets the text for the tab (Note: this also sets the tooltip text)
29962      * @param {String} text The tab's text and tooltip
29963      */
29964     setText : function(text){
29965         this.text = text;
29966         this.textEl.update(text);
29967         this.setTooltip(text);
29968         if(!this.tabPanel.resizeTabs){
29969             this.autoSize();
29970         }
29971     },
29972     /**
29973      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29974      */
29975     activate : function(){
29976         this.tabPanel.activate(this.id);
29977     },
29978
29979     /**
29980      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29981      */
29982     disable : function(){
29983         if(this.tabPanel.active != this){
29984             this.disabled = true;
29985             this.pnode.addClass("disabled");
29986         }
29987     },
29988
29989     /**
29990      * Enables this TabPanelItem if it was previously disabled.
29991      */
29992     enable : function(){
29993         this.disabled = false;
29994         this.pnode.removeClass("disabled");
29995     },
29996
29997     /**
29998      * Sets the content for this TabPanelItem.
29999      * @param {String} content The content
30000      * @param {Boolean} loadScripts true to look for and load scripts
30001      */
30002     setContent : function(content, loadScripts){
30003         this.bodyEl.update(content, loadScripts);
30004     },
30005
30006     /**
30007      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30008      * @return {Roo.UpdateManager} The UpdateManager
30009      */
30010     getUpdateManager : function(){
30011         return this.bodyEl.getUpdateManager();
30012     },
30013
30014     /**
30015      * Set a URL to be used to load the content for this TabPanelItem.
30016      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30017      * @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)
30018      * @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)
30019      * @return {Roo.UpdateManager} The UpdateManager
30020      */
30021     setUrl : function(url, params, loadOnce){
30022         if(this.refreshDelegate){
30023             this.un('activate', this.refreshDelegate);
30024         }
30025         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30026         this.on("activate", this.refreshDelegate);
30027         return this.bodyEl.getUpdateManager();
30028     },
30029
30030     /** @private */
30031     _handleRefresh : function(url, params, loadOnce){
30032         if(!loadOnce || !this.loaded){
30033             var updater = this.bodyEl.getUpdateManager();
30034             updater.update(url, params, this._setLoaded.createDelegate(this));
30035         }
30036     },
30037
30038     /**
30039      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30040      *   Will fail silently if the setUrl method has not been called.
30041      *   This does not activate the panel, just updates its content.
30042      */
30043     refresh : function(){
30044         if(this.refreshDelegate){
30045            this.loaded = false;
30046            this.refreshDelegate();
30047         }
30048     },
30049
30050     /** @private */
30051     _setLoaded : function(){
30052         this.loaded = true;
30053     },
30054
30055     /** @private */
30056     closeClick : function(e){
30057         var o = {};
30058         e.stopEvent();
30059         this.fireEvent("beforeclose", this, o);
30060         if(o.cancel !== true){
30061             this.tabPanel.removeTab(this.id);
30062         }
30063     },
30064     /**
30065      * The text displayed in the tooltip for the close icon.
30066      * @type String
30067      */
30068     closeText : "Close this tab"
30069 });
30070
30071 /** @private */
30072 Roo.TabPanel.prototype.createStrip = function(container){
30073     var strip = document.createElement("div");
30074     strip.className = "x-tabs-wrap";
30075     container.appendChild(strip);
30076     return strip;
30077 };
30078 /** @private */
30079 Roo.TabPanel.prototype.createStripList = function(strip){
30080     // div wrapper for retard IE
30081     // returns the "tr" element.
30082     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30083         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30084         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30085     return strip.firstChild.firstChild.firstChild.firstChild;
30086 };
30087 /** @private */
30088 Roo.TabPanel.prototype.createBody = function(container){
30089     var body = document.createElement("div");
30090     Roo.id(body, "tab-body");
30091     Roo.fly(body).addClass("x-tabs-body");
30092     container.appendChild(body);
30093     return body;
30094 };
30095 /** @private */
30096 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30097     var body = Roo.getDom(id);
30098     if(!body){
30099         body = document.createElement("div");
30100         body.id = id;
30101     }
30102     Roo.fly(body).addClass("x-tabs-item-body");
30103     bodyEl.insertBefore(body, bodyEl.firstChild);
30104     return body;
30105 };
30106 /** @private */
30107 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30108     var td = document.createElement("td");
30109     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30110     //stripEl.appendChild(td);
30111     if(closable){
30112         td.className = "x-tabs-closable";
30113         if(!this.closeTpl){
30114             this.closeTpl = new Roo.Template(
30115                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30116                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30117                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30118             );
30119         }
30120         var el = this.closeTpl.overwrite(td, {"text": text});
30121         var close = el.getElementsByTagName("div")[0];
30122         var inner = el.getElementsByTagName("em")[0];
30123         return {"el": el, "close": close, "inner": inner};
30124     } else {
30125         if(!this.tabTpl){
30126             this.tabTpl = new Roo.Template(
30127                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30128                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30129             );
30130         }
30131         var el = this.tabTpl.overwrite(td, {"text": text});
30132         var inner = el.getElementsByTagName("em")[0];
30133         return {"el": el, "inner": inner};
30134     }
30135 };/*
30136  * Based on:
30137  * Ext JS Library 1.1.1
30138  * Copyright(c) 2006-2007, Ext JS, LLC.
30139  *
30140  * Originally Released Under LGPL - original licence link has changed is not relivant.
30141  *
30142  * Fork - LGPL
30143  * <script type="text/javascript">
30144  */
30145
30146 /**
30147  * @class Roo.Button
30148  * @extends Roo.util.Observable
30149  * Simple Button class
30150  * @cfg {String} text The button text
30151  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30152  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30153  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30154  * @cfg {Object} scope The scope of the handler
30155  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30156  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30157  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30158  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30159  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30160  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30161    applies if enableToggle = true)
30162  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30163  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30164   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30165  * @constructor
30166  * Create a new button
30167  * @param {Object} config The config object
30168  */
30169 Roo.Button = function(renderTo, config)
30170 {
30171     if (!config) {
30172         config = renderTo;
30173         renderTo = config.renderTo || false;
30174     }
30175     
30176     Roo.apply(this, config);
30177     this.addEvents({
30178         /**
30179              * @event click
30180              * Fires when this button is clicked
30181              * @param {Button} this
30182              * @param {EventObject} e The click event
30183              */
30184             "click" : true,
30185         /**
30186              * @event toggle
30187              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30188              * @param {Button} this
30189              * @param {Boolean} pressed
30190              */
30191             "toggle" : true,
30192         /**
30193              * @event mouseover
30194              * Fires when the mouse hovers over the button
30195              * @param {Button} this
30196              * @param {Event} e The event object
30197              */
30198         'mouseover' : true,
30199         /**
30200              * @event mouseout
30201              * Fires when the mouse exits the button
30202              * @param {Button} this
30203              * @param {Event} e The event object
30204              */
30205         'mouseout': true,
30206          /**
30207              * @event render
30208              * Fires when the button is rendered
30209              * @param {Button} this
30210              */
30211         'render': true
30212     });
30213     if(this.menu){
30214         this.menu = Roo.menu.MenuMgr.get(this.menu);
30215     }
30216     // register listeners first!!  - so render can be captured..
30217     Roo.util.Observable.call(this);
30218     if(renderTo){
30219         this.render(renderTo);
30220     }
30221     
30222   
30223 };
30224
30225 Roo.extend(Roo.Button, Roo.util.Observable, {
30226     /**
30227      * 
30228      */
30229     
30230     /**
30231      * Read-only. True if this button is hidden
30232      * @type Boolean
30233      */
30234     hidden : false,
30235     /**
30236      * Read-only. True if this button is disabled
30237      * @type Boolean
30238      */
30239     disabled : false,
30240     /**
30241      * Read-only. True if this button is pressed (only if enableToggle = true)
30242      * @type Boolean
30243      */
30244     pressed : false,
30245
30246     /**
30247      * @cfg {Number} tabIndex 
30248      * The DOM tabIndex for this button (defaults to undefined)
30249      */
30250     tabIndex : undefined,
30251
30252     /**
30253      * @cfg {Boolean} enableToggle
30254      * True to enable pressed/not pressed toggling (defaults to false)
30255      */
30256     enableToggle: false,
30257     /**
30258      * @cfg {Roo.menu.Menu} menu
30259      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30260      */
30261     menu : undefined,
30262     /**
30263      * @cfg {String} menuAlign
30264      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30265      */
30266     menuAlign : "tl-bl?",
30267
30268     /**
30269      * @cfg {String} iconCls
30270      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30271      */
30272     iconCls : undefined,
30273     /**
30274      * @cfg {String} type
30275      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30276      */
30277     type : 'button',
30278
30279     // private
30280     menuClassTarget: 'tr',
30281
30282     /**
30283      * @cfg {String} clickEvent
30284      * The type of event to map to the button's event handler (defaults to 'click')
30285      */
30286     clickEvent : 'click',
30287
30288     /**
30289      * @cfg {Boolean} handleMouseEvents
30290      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30291      */
30292     handleMouseEvents : true,
30293
30294     /**
30295      * @cfg {String} tooltipType
30296      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30297      */
30298     tooltipType : 'qtip',
30299
30300     /**
30301      * @cfg {String} cls
30302      * A CSS class to apply to the button's main element.
30303      */
30304     
30305     /**
30306      * @cfg {Roo.Template} template (Optional)
30307      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30308      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30309      * require code modifications if required elements (e.g. a button) aren't present.
30310      */
30311
30312     // private
30313     render : function(renderTo){
30314         var btn;
30315         if(this.hideParent){
30316             this.parentEl = Roo.get(renderTo);
30317         }
30318         if(!this.dhconfig){
30319             if(!this.template){
30320                 if(!Roo.Button.buttonTemplate){
30321                     // hideous table template
30322                     Roo.Button.buttonTemplate = new Roo.Template(
30323                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30324                         '<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>',
30325                         "</tr></tbody></table>");
30326                 }
30327                 this.template = Roo.Button.buttonTemplate;
30328             }
30329             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30330             var btnEl = btn.child("button:first");
30331             btnEl.on('focus', this.onFocus, this);
30332             btnEl.on('blur', this.onBlur, this);
30333             if(this.cls){
30334                 btn.addClass(this.cls);
30335             }
30336             if(this.icon){
30337                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30338             }
30339             if(this.iconCls){
30340                 btnEl.addClass(this.iconCls);
30341                 if(!this.cls){
30342                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30343                 }
30344             }
30345             if(this.tabIndex !== undefined){
30346                 btnEl.dom.tabIndex = this.tabIndex;
30347             }
30348             if(this.tooltip){
30349                 if(typeof this.tooltip == 'object'){
30350                     Roo.QuickTips.tips(Roo.apply({
30351                           target: btnEl.id
30352                     }, this.tooltip));
30353                 } else {
30354                     btnEl.dom[this.tooltipType] = this.tooltip;
30355                 }
30356             }
30357         }else{
30358             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30359         }
30360         this.el = btn;
30361         if(this.id){
30362             this.el.dom.id = this.el.id = this.id;
30363         }
30364         if(this.menu){
30365             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30366             this.menu.on("show", this.onMenuShow, this);
30367             this.menu.on("hide", this.onMenuHide, this);
30368         }
30369         btn.addClass("x-btn");
30370         if(Roo.isIE && !Roo.isIE7){
30371             this.autoWidth.defer(1, this);
30372         }else{
30373             this.autoWidth();
30374         }
30375         if(this.handleMouseEvents){
30376             btn.on("mouseover", this.onMouseOver, this);
30377             btn.on("mouseout", this.onMouseOut, this);
30378             btn.on("mousedown", this.onMouseDown, this);
30379         }
30380         btn.on(this.clickEvent, this.onClick, this);
30381         //btn.on("mouseup", this.onMouseUp, this);
30382         if(this.hidden){
30383             this.hide();
30384         }
30385         if(this.disabled){
30386             this.disable();
30387         }
30388         Roo.ButtonToggleMgr.register(this);
30389         if(this.pressed){
30390             this.el.addClass("x-btn-pressed");
30391         }
30392         if(this.repeat){
30393             var repeater = new Roo.util.ClickRepeater(btn,
30394                 typeof this.repeat == "object" ? this.repeat : {}
30395             );
30396             repeater.on("click", this.onClick,  this);
30397         }
30398         
30399         this.fireEvent('render', this);
30400         
30401     },
30402     /**
30403      * Returns the button's underlying element
30404      * @return {Roo.Element} The element
30405      */
30406     getEl : function(){
30407         return this.el;  
30408     },
30409     
30410     /**
30411      * Destroys this Button and removes any listeners.
30412      */
30413     destroy : function(){
30414         Roo.ButtonToggleMgr.unregister(this);
30415         this.el.removeAllListeners();
30416         this.purgeListeners();
30417         this.el.remove();
30418     },
30419
30420     // private
30421     autoWidth : function(){
30422         if(this.el){
30423             this.el.setWidth("auto");
30424             if(Roo.isIE7 && Roo.isStrict){
30425                 var ib = this.el.child('button');
30426                 if(ib && ib.getWidth() > 20){
30427                     ib.clip();
30428                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30429                 }
30430             }
30431             if(this.minWidth){
30432                 if(this.hidden){
30433                     this.el.beginMeasure();
30434                 }
30435                 if(this.el.getWidth() < this.minWidth){
30436                     this.el.setWidth(this.minWidth);
30437                 }
30438                 if(this.hidden){
30439                     this.el.endMeasure();
30440                 }
30441             }
30442         }
30443     },
30444
30445     /**
30446      * Assigns this button's click handler
30447      * @param {Function} handler The function to call when the button is clicked
30448      * @param {Object} scope (optional) Scope for the function passed in
30449      */
30450     setHandler : function(handler, scope){
30451         this.handler = handler;
30452         this.scope = scope;  
30453     },
30454     
30455     /**
30456      * Sets this button's text
30457      * @param {String} text The button text
30458      */
30459     setText : function(text){
30460         this.text = text;
30461         if(this.el){
30462             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30463         }
30464         this.autoWidth();
30465     },
30466     
30467     /**
30468      * Gets the text for this button
30469      * @return {String} The button text
30470      */
30471     getText : function(){
30472         return this.text;  
30473     },
30474     
30475     /**
30476      * Show this button
30477      */
30478     show: function(){
30479         this.hidden = false;
30480         if(this.el){
30481             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30482         }
30483     },
30484     
30485     /**
30486      * Hide this button
30487      */
30488     hide: function(){
30489         this.hidden = true;
30490         if(this.el){
30491             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30492         }
30493     },
30494     
30495     /**
30496      * Convenience function for boolean show/hide
30497      * @param {Boolean} visible True to show, false to hide
30498      */
30499     setVisible: function(visible){
30500         if(visible) {
30501             this.show();
30502         }else{
30503             this.hide();
30504         }
30505     },
30506     
30507     /**
30508      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30509      * @param {Boolean} state (optional) Force a particular state
30510      */
30511     toggle : function(state){
30512         state = state === undefined ? !this.pressed : state;
30513         if(state != this.pressed){
30514             if(state){
30515                 this.el.addClass("x-btn-pressed");
30516                 this.pressed = true;
30517                 this.fireEvent("toggle", this, true);
30518             }else{
30519                 this.el.removeClass("x-btn-pressed");
30520                 this.pressed = false;
30521                 this.fireEvent("toggle", this, false);
30522             }
30523             if(this.toggleHandler){
30524                 this.toggleHandler.call(this.scope || this, this, state);
30525             }
30526         }
30527     },
30528     
30529     /**
30530      * Focus the button
30531      */
30532     focus : function(){
30533         this.el.child('button:first').focus();
30534     },
30535     
30536     /**
30537      * Disable this button
30538      */
30539     disable : function(){
30540         if(this.el){
30541             this.el.addClass("x-btn-disabled");
30542         }
30543         this.disabled = true;
30544     },
30545     
30546     /**
30547      * Enable this button
30548      */
30549     enable : function(){
30550         if(this.el){
30551             this.el.removeClass("x-btn-disabled");
30552         }
30553         this.disabled = false;
30554     },
30555
30556     /**
30557      * Convenience function for boolean enable/disable
30558      * @param {Boolean} enabled True to enable, false to disable
30559      */
30560     setDisabled : function(v){
30561         this[v !== true ? "enable" : "disable"]();
30562     },
30563
30564     // private
30565     onClick : function(e)
30566     {
30567         if(e){
30568             e.preventDefault();
30569         }
30570         if(e.button != 0){
30571             return;
30572         }
30573         if(!this.disabled){
30574             if(this.enableToggle){
30575                 this.toggle();
30576             }
30577             if(this.menu && !this.menu.isVisible()){
30578                 this.menu.show(this.el, this.menuAlign);
30579             }
30580             this.fireEvent("click", this, e);
30581             if(this.handler){
30582                 this.el.removeClass("x-btn-over");
30583                 this.handler.call(this.scope || this, this, e);
30584             }
30585         }
30586     },
30587     // private
30588     onMouseOver : function(e){
30589         if(!this.disabled){
30590             this.el.addClass("x-btn-over");
30591             this.fireEvent('mouseover', this, e);
30592         }
30593     },
30594     // private
30595     onMouseOut : function(e){
30596         if(!e.within(this.el,  true)){
30597             this.el.removeClass("x-btn-over");
30598             this.fireEvent('mouseout', this, e);
30599         }
30600     },
30601     // private
30602     onFocus : function(e){
30603         if(!this.disabled){
30604             this.el.addClass("x-btn-focus");
30605         }
30606     },
30607     // private
30608     onBlur : function(e){
30609         this.el.removeClass("x-btn-focus");
30610     },
30611     // private
30612     onMouseDown : function(e){
30613         if(!this.disabled && e.button == 0){
30614             this.el.addClass("x-btn-click");
30615             Roo.get(document).on('mouseup', this.onMouseUp, this);
30616         }
30617     },
30618     // private
30619     onMouseUp : function(e){
30620         if(e.button == 0){
30621             this.el.removeClass("x-btn-click");
30622             Roo.get(document).un('mouseup', this.onMouseUp, this);
30623         }
30624     },
30625     // private
30626     onMenuShow : function(e){
30627         this.el.addClass("x-btn-menu-active");
30628     },
30629     // private
30630     onMenuHide : function(e){
30631         this.el.removeClass("x-btn-menu-active");
30632     }   
30633 });
30634
30635 // Private utility class used by Button
30636 Roo.ButtonToggleMgr = function(){
30637    var groups = {};
30638    
30639    function toggleGroup(btn, state){
30640        if(state){
30641            var g = groups[btn.toggleGroup];
30642            for(var i = 0, l = g.length; i < l; i++){
30643                if(g[i] != btn){
30644                    g[i].toggle(false);
30645                }
30646            }
30647        }
30648    }
30649    
30650    return {
30651        register : function(btn){
30652            if(!btn.toggleGroup){
30653                return;
30654            }
30655            var g = groups[btn.toggleGroup];
30656            if(!g){
30657                g = groups[btn.toggleGroup] = [];
30658            }
30659            g.push(btn);
30660            btn.on("toggle", toggleGroup);
30661        },
30662        
30663        unregister : function(btn){
30664            if(!btn.toggleGroup){
30665                return;
30666            }
30667            var g = groups[btn.toggleGroup];
30668            if(g){
30669                g.remove(btn);
30670                btn.un("toggle", toggleGroup);
30671            }
30672        }
30673    };
30674 }();/*
30675  * Based on:
30676  * Ext JS Library 1.1.1
30677  * Copyright(c) 2006-2007, Ext JS, LLC.
30678  *
30679  * Originally Released Under LGPL - original licence link has changed is not relivant.
30680  *
30681  * Fork - LGPL
30682  * <script type="text/javascript">
30683  */
30684  
30685 /**
30686  * @class Roo.SplitButton
30687  * @extends Roo.Button
30688  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30689  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30690  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30691  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30692  * @cfg {String} arrowTooltip The title attribute of the arrow
30693  * @constructor
30694  * Create a new menu button
30695  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30696  * @param {Object} config The config object
30697  */
30698 Roo.SplitButton = function(renderTo, config){
30699     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30700     /**
30701      * @event arrowclick
30702      * Fires when this button's arrow is clicked
30703      * @param {SplitButton} this
30704      * @param {EventObject} e The click event
30705      */
30706     this.addEvents({"arrowclick":true});
30707 };
30708
30709 Roo.extend(Roo.SplitButton, Roo.Button, {
30710     render : function(renderTo){
30711         // this is one sweet looking template!
30712         var tpl = new Roo.Template(
30713             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30714             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30715             '<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>',
30716             "</tbody></table></td><td>",
30717             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30718             '<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>',
30719             "</tbody></table></td></tr></table>"
30720         );
30721         var btn = tpl.append(renderTo, [this.text, this.type], true);
30722         var btnEl = btn.child("button");
30723         if(this.cls){
30724             btn.addClass(this.cls);
30725         }
30726         if(this.icon){
30727             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30728         }
30729         if(this.iconCls){
30730             btnEl.addClass(this.iconCls);
30731             if(!this.cls){
30732                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30733             }
30734         }
30735         this.el = btn;
30736         if(this.handleMouseEvents){
30737             btn.on("mouseover", this.onMouseOver, this);
30738             btn.on("mouseout", this.onMouseOut, this);
30739             btn.on("mousedown", this.onMouseDown, this);
30740             btn.on("mouseup", this.onMouseUp, this);
30741         }
30742         btn.on(this.clickEvent, this.onClick, this);
30743         if(this.tooltip){
30744             if(typeof this.tooltip == 'object'){
30745                 Roo.QuickTips.tips(Roo.apply({
30746                       target: btnEl.id
30747                 }, this.tooltip));
30748             } else {
30749                 btnEl.dom[this.tooltipType] = this.tooltip;
30750             }
30751         }
30752         if(this.arrowTooltip){
30753             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30754         }
30755         if(this.hidden){
30756             this.hide();
30757         }
30758         if(this.disabled){
30759             this.disable();
30760         }
30761         if(this.pressed){
30762             this.el.addClass("x-btn-pressed");
30763         }
30764         if(Roo.isIE && !Roo.isIE7){
30765             this.autoWidth.defer(1, this);
30766         }else{
30767             this.autoWidth();
30768         }
30769         if(this.menu){
30770             this.menu.on("show", this.onMenuShow, this);
30771             this.menu.on("hide", this.onMenuHide, this);
30772         }
30773         this.fireEvent('render', this);
30774     },
30775
30776     // private
30777     autoWidth : function(){
30778         if(this.el){
30779             var tbl = this.el.child("table:first");
30780             var tbl2 = this.el.child("table:last");
30781             this.el.setWidth("auto");
30782             tbl.setWidth("auto");
30783             if(Roo.isIE7 && Roo.isStrict){
30784                 var ib = this.el.child('button:first');
30785                 if(ib && ib.getWidth() > 20){
30786                     ib.clip();
30787                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30788                 }
30789             }
30790             if(this.minWidth){
30791                 if(this.hidden){
30792                     this.el.beginMeasure();
30793                 }
30794                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30795                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30796                 }
30797                 if(this.hidden){
30798                     this.el.endMeasure();
30799                 }
30800             }
30801             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30802         } 
30803     },
30804     /**
30805      * Sets this button's click handler
30806      * @param {Function} handler The function to call when the button is clicked
30807      * @param {Object} scope (optional) Scope for the function passed above
30808      */
30809     setHandler : function(handler, scope){
30810         this.handler = handler;
30811         this.scope = scope;  
30812     },
30813     
30814     /**
30815      * Sets this button's arrow click handler
30816      * @param {Function} handler The function to call when the arrow is clicked
30817      * @param {Object} scope (optional) Scope for the function passed above
30818      */
30819     setArrowHandler : function(handler, scope){
30820         this.arrowHandler = handler;
30821         this.scope = scope;  
30822     },
30823     
30824     /**
30825      * Focus the button
30826      */
30827     focus : function(){
30828         if(this.el){
30829             this.el.child("button:first").focus();
30830         }
30831     },
30832
30833     // private
30834     onClick : function(e){
30835         e.preventDefault();
30836         if(!this.disabled){
30837             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30838                 if(this.menu && !this.menu.isVisible()){
30839                     this.menu.show(this.el, this.menuAlign);
30840                 }
30841                 this.fireEvent("arrowclick", this, e);
30842                 if(this.arrowHandler){
30843                     this.arrowHandler.call(this.scope || this, this, e);
30844                 }
30845             }else{
30846                 this.fireEvent("click", this, e);
30847                 if(this.handler){
30848                     this.handler.call(this.scope || this, this, e);
30849                 }
30850             }
30851         }
30852     },
30853     // private
30854     onMouseDown : function(e){
30855         if(!this.disabled){
30856             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30857         }
30858     },
30859     // private
30860     onMouseUp : function(e){
30861         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30862     }   
30863 });
30864
30865
30866 // backwards compat
30867 Roo.MenuButton = Roo.SplitButton;/*
30868  * Based on:
30869  * Ext JS Library 1.1.1
30870  * Copyright(c) 2006-2007, Ext JS, LLC.
30871  *
30872  * Originally Released Under LGPL - original licence link has changed is not relivant.
30873  *
30874  * Fork - LGPL
30875  * <script type="text/javascript">
30876  */
30877
30878 /**
30879  * @class Roo.Toolbar
30880  * @children   Roo.Toolbar.Item Roo.form.Field
30881  * Basic Toolbar class.
30882  * @constructor
30883  * Creates a new Toolbar
30884  * @param {Object} container The config object
30885  */ 
30886 Roo.Toolbar = function(container, buttons, config)
30887 {
30888     /// old consturctor format still supported..
30889     if(container instanceof Array){ // omit the container for later rendering
30890         buttons = container;
30891         config = buttons;
30892         container = null;
30893     }
30894     if (typeof(container) == 'object' && container.xtype) {
30895         config = container;
30896         container = config.container;
30897         buttons = config.buttons || []; // not really - use items!!
30898     }
30899     var xitems = [];
30900     if (config && config.items) {
30901         xitems = config.items;
30902         delete config.items;
30903     }
30904     Roo.apply(this, config);
30905     this.buttons = buttons;
30906     
30907     if(container){
30908         this.render(container);
30909     }
30910     this.xitems = xitems;
30911     Roo.each(xitems, function(b) {
30912         this.add(b);
30913     }, this);
30914     
30915 };
30916
30917 Roo.Toolbar.prototype = {
30918     /**
30919      * @cfg {Array} items
30920      * array of button configs or elements to add (will be converted to a MixedCollection)
30921      */
30922     items: false,
30923     /**
30924      * @cfg {String/HTMLElement/Element} container
30925      * The id or element that will contain the toolbar
30926      */
30927     // private
30928     render : function(ct){
30929         this.el = Roo.get(ct);
30930         if(this.cls){
30931             this.el.addClass(this.cls);
30932         }
30933         // using a table allows for vertical alignment
30934         // 100% width is needed by Safari...
30935         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30936         this.tr = this.el.child("tr", true);
30937         var autoId = 0;
30938         this.items = new Roo.util.MixedCollection(false, function(o){
30939             return o.id || ("item" + (++autoId));
30940         });
30941         if(this.buttons){
30942             this.add.apply(this, this.buttons);
30943             delete this.buttons;
30944         }
30945     },
30946
30947     /**
30948      * Adds element(s) to the toolbar -- this function takes a variable number of 
30949      * arguments of mixed type and adds them to the toolbar.
30950      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30951      * <ul>
30952      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30953      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30954      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30955      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30956      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30957      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30958      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30959      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30960      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30961      * </ul>
30962      * @param {Mixed} arg2
30963      * @param {Mixed} etc.
30964      */
30965     add : function(){
30966         var a = arguments, l = a.length;
30967         for(var i = 0; i < l; i++){
30968             this._add(a[i]);
30969         }
30970     },
30971     // private..
30972     _add : function(el) {
30973         
30974         if (el.xtype) {
30975             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30976         }
30977         
30978         if (el.applyTo){ // some kind of form field
30979             return this.addField(el);
30980         } 
30981         if (el.render){ // some kind of Toolbar.Item
30982             return this.addItem(el);
30983         }
30984         if (typeof el == "string"){ // string
30985             if(el == "separator" || el == "-"){
30986                 return this.addSeparator();
30987             }
30988             if (el == " "){
30989                 return this.addSpacer();
30990             }
30991             if(el == "->"){
30992                 return this.addFill();
30993             }
30994             return this.addText(el);
30995             
30996         }
30997         if(el.tagName){ // element
30998             return this.addElement(el);
30999         }
31000         if(typeof el == "object"){ // must be button config?
31001             return this.addButton(el);
31002         }
31003         // and now what?!?!
31004         return false;
31005         
31006     },
31007     
31008     /**
31009      * Add an Xtype element
31010      * @param {Object} xtype Xtype Object
31011      * @return {Object} created Object
31012      */
31013     addxtype : function(e){
31014         return this.add(e);  
31015     },
31016     
31017     /**
31018      * Returns the Element for this toolbar.
31019      * @return {Roo.Element}
31020      */
31021     getEl : function(){
31022         return this.el;  
31023     },
31024     
31025     /**
31026      * Adds a separator
31027      * @return {Roo.Toolbar.Item} The separator item
31028      */
31029     addSeparator : function(){
31030         return this.addItem(new Roo.Toolbar.Separator());
31031     },
31032
31033     /**
31034      * Adds a spacer element
31035      * @return {Roo.Toolbar.Spacer} The spacer item
31036      */
31037     addSpacer : function(){
31038         return this.addItem(new Roo.Toolbar.Spacer());
31039     },
31040
31041     /**
31042      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31043      * @return {Roo.Toolbar.Fill} The fill item
31044      */
31045     addFill : function(){
31046         return this.addItem(new Roo.Toolbar.Fill());
31047     },
31048
31049     /**
31050      * Adds any standard HTML element to the toolbar
31051      * @param {String/HTMLElement/Element} el The element or id of the element to add
31052      * @return {Roo.Toolbar.Item} The element's item
31053      */
31054     addElement : function(el){
31055         return this.addItem(new Roo.Toolbar.Item(el));
31056     },
31057     /**
31058      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31059      * @type Roo.util.MixedCollection  
31060      */
31061     items : false,
31062      
31063     /**
31064      * Adds any Toolbar.Item or subclass
31065      * @param {Roo.Toolbar.Item} item
31066      * @return {Roo.Toolbar.Item} The item
31067      */
31068     addItem : function(item){
31069         var td = this.nextBlock();
31070         item.render(td);
31071         this.items.add(item);
31072         return item;
31073     },
31074     
31075     /**
31076      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31077      * @param {Object/Array} config A button config or array of configs
31078      * @return {Roo.Toolbar.Button/Array}
31079      */
31080     addButton : function(config){
31081         if(config instanceof Array){
31082             var buttons = [];
31083             for(var i = 0, len = config.length; i < len; i++) {
31084                 buttons.push(this.addButton(config[i]));
31085             }
31086             return buttons;
31087         }
31088         var b = config;
31089         if(!(config instanceof Roo.Toolbar.Button)){
31090             b = config.split ?
31091                 new Roo.Toolbar.SplitButton(config) :
31092                 new Roo.Toolbar.Button(config);
31093         }
31094         var td = this.nextBlock();
31095         b.render(td);
31096         this.items.add(b);
31097         return b;
31098     },
31099     
31100     /**
31101      * Adds text to the toolbar
31102      * @param {String} text The text to add
31103      * @return {Roo.Toolbar.Item} The element's item
31104      */
31105     addText : function(text){
31106         return this.addItem(new Roo.Toolbar.TextItem(text));
31107     },
31108     
31109     /**
31110      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31111      * @param {Number} index The index where the item is to be inserted
31112      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31113      * @return {Roo.Toolbar.Button/Item}
31114      */
31115     insertButton : function(index, item){
31116         if(item instanceof Array){
31117             var buttons = [];
31118             for(var i = 0, len = item.length; i < len; i++) {
31119                buttons.push(this.insertButton(index + i, item[i]));
31120             }
31121             return buttons;
31122         }
31123         if (!(item instanceof Roo.Toolbar.Button)){
31124            item = new Roo.Toolbar.Button(item);
31125         }
31126         var td = document.createElement("td");
31127         this.tr.insertBefore(td, this.tr.childNodes[index]);
31128         item.render(td);
31129         this.items.insert(index, item);
31130         return item;
31131     },
31132     
31133     /**
31134      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31135      * @param {Object} config
31136      * @return {Roo.Toolbar.Item} The element's item
31137      */
31138     addDom : function(config, returnEl){
31139         var td = this.nextBlock();
31140         Roo.DomHelper.overwrite(td, config);
31141         var ti = new Roo.Toolbar.Item(td.firstChild);
31142         ti.render(td);
31143         this.items.add(ti);
31144         return ti;
31145     },
31146
31147     /**
31148      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31149      * @type Roo.util.MixedCollection  
31150      */
31151     fields : false,
31152     
31153     /**
31154      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31155      * Note: the field should not have been rendered yet. For a field that has already been
31156      * rendered, use {@link #addElement}.
31157      * @param {Roo.form.Field} field
31158      * @return {Roo.ToolbarItem}
31159      */
31160      
31161       
31162     addField : function(field) {
31163         if (!this.fields) {
31164             var autoId = 0;
31165             this.fields = new Roo.util.MixedCollection(false, function(o){
31166                 return o.id || ("item" + (++autoId));
31167             });
31168
31169         }
31170         
31171         var td = this.nextBlock();
31172         field.render(td);
31173         var ti = new Roo.Toolbar.Item(td.firstChild);
31174         ti.render(td);
31175         this.items.add(ti);
31176         this.fields.add(field);
31177         return ti;
31178     },
31179     /**
31180      * Hide the toolbar
31181      * @method hide
31182      */
31183      
31184       
31185     hide : function()
31186     {
31187         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31188         this.el.child('div').hide();
31189     },
31190     /**
31191      * Show the toolbar
31192      * @method show
31193      */
31194     show : function()
31195     {
31196         this.el.child('div').show();
31197     },
31198       
31199     // private
31200     nextBlock : function(){
31201         var td = document.createElement("td");
31202         this.tr.appendChild(td);
31203         return td;
31204     },
31205
31206     // private
31207     destroy : function(){
31208         if(this.items){ // rendered?
31209             Roo.destroy.apply(Roo, this.items.items);
31210         }
31211         if(this.fields){ // rendered?
31212             Roo.destroy.apply(Roo, this.fields.items);
31213         }
31214         Roo.Element.uncache(this.el, this.tr);
31215     }
31216 };
31217
31218 /**
31219  * @class Roo.Toolbar.Item
31220  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31221  * @constructor
31222  * Creates a new Item
31223  * @param {HTMLElement} el 
31224  */
31225 Roo.Toolbar.Item = function(el){
31226     var cfg = {};
31227     if (typeof (el.xtype) != 'undefined') {
31228         cfg = el;
31229         el = cfg.el;
31230     }
31231     
31232     this.el = Roo.getDom(el);
31233     this.id = Roo.id(this.el);
31234     this.hidden = false;
31235     
31236     this.addEvents({
31237          /**
31238              * @event render
31239              * Fires when the button is rendered
31240              * @param {Button} this
31241              */
31242         'render': true
31243     });
31244     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31245 };
31246 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31247 //Roo.Toolbar.Item.prototype = {
31248     
31249     /**
31250      * Get this item's HTML Element
31251      * @return {HTMLElement}
31252      */
31253     getEl : function(){
31254        return this.el;  
31255     },
31256
31257     // private
31258     render : function(td){
31259         
31260          this.td = td;
31261         td.appendChild(this.el);
31262         
31263         this.fireEvent('render', this);
31264     },
31265     
31266     /**
31267      * Removes and destroys this item.
31268      */
31269     destroy : function(){
31270         this.td.parentNode.removeChild(this.td);
31271     },
31272     
31273     /**
31274      * Shows this item.
31275      */
31276     show: function(){
31277         this.hidden = false;
31278         this.td.style.display = "";
31279     },
31280     
31281     /**
31282      * Hides this item.
31283      */
31284     hide: function(){
31285         this.hidden = true;
31286         this.td.style.display = "none";
31287     },
31288     
31289     /**
31290      * Convenience function for boolean show/hide.
31291      * @param {Boolean} visible true to show/false to hide
31292      */
31293     setVisible: function(visible){
31294         if(visible) {
31295             this.show();
31296         }else{
31297             this.hide();
31298         }
31299     },
31300     
31301     /**
31302      * Try to focus this item.
31303      */
31304     focus : function(){
31305         Roo.fly(this.el).focus();
31306     },
31307     
31308     /**
31309      * Disables this item.
31310      */
31311     disable : function(){
31312         Roo.fly(this.td).addClass("x-item-disabled");
31313         this.disabled = true;
31314         this.el.disabled = true;
31315     },
31316     
31317     /**
31318      * Enables this item.
31319      */
31320     enable : function(){
31321         Roo.fly(this.td).removeClass("x-item-disabled");
31322         this.disabled = false;
31323         this.el.disabled = false;
31324     }
31325 });
31326
31327
31328 /**
31329  * @class Roo.Toolbar.Separator
31330  * @extends Roo.Toolbar.Item
31331  * A simple toolbar separator class
31332  * @constructor
31333  * Creates a new Separator
31334  */
31335 Roo.Toolbar.Separator = function(cfg){
31336     
31337     var s = document.createElement("span");
31338     s.className = "ytb-sep";
31339     if (cfg) {
31340         cfg.el = s;
31341     }
31342     
31343     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31344 };
31345 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31346     enable:Roo.emptyFn,
31347     disable:Roo.emptyFn,
31348     focus:Roo.emptyFn
31349 });
31350
31351 /**
31352  * @class Roo.Toolbar.Spacer
31353  * @extends Roo.Toolbar.Item
31354  * A simple element that adds extra horizontal space to a toolbar.
31355  * @constructor
31356  * Creates a new Spacer
31357  */
31358 Roo.Toolbar.Spacer = function(cfg){
31359     var s = document.createElement("div");
31360     s.className = "ytb-spacer";
31361     if (cfg) {
31362         cfg.el = s;
31363     }
31364     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31365 };
31366 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31367     enable:Roo.emptyFn,
31368     disable:Roo.emptyFn,
31369     focus:Roo.emptyFn
31370 });
31371
31372 /**
31373  * @class Roo.Toolbar.Fill
31374  * @extends Roo.Toolbar.Spacer
31375  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31376  * @constructor
31377  * Creates a new Spacer
31378  */
31379 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31380     // private
31381     render : function(td){
31382         td.style.width = '100%';
31383         Roo.Toolbar.Fill.superclass.render.call(this, td);
31384     }
31385 });
31386
31387 /**
31388  * @class Roo.Toolbar.TextItem
31389  * @extends Roo.Toolbar.Item
31390  * A simple class that renders text directly into a toolbar.
31391  * @constructor
31392  * Creates a new TextItem
31393  * @cfg {string} text 
31394  */
31395 Roo.Toolbar.TextItem = function(cfg){
31396     var  text = cfg || "";
31397     if (typeof(cfg) == 'object') {
31398         text = cfg.text || "";
31399     }  else {
31400         cfg = null;
31401     }
31402     var s = document.createElement("span");
31403     s.className = "ytb-text";
31404     s.innerHTML = text;
31405     if (cfg) {
31406         cfg.el  = s;
31407     }
31408     
31409     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31410 };
31411 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31412     
31413      
31414     enable:Roo.emptyFn,
31415     disable:Roo.emptyFn,
31416     focus:Roo.emptyFn
31417 });
31418
31419 /**
31420  * @class Roo.Toolbar.Button
31421  * @extends Roo.Button
31422  * A button that renders into a toolbar.
31423  * @constructor
31424  * Creates a new Button
31425  * @param {Object} config A standard {@link Roo.Button} config object
31426  */
31427 Roo.Toolbar.Button = function(config){
31428     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31429 };
31430 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31431 {
31432     
31433     
31434     render : function(td){
31435         this.td = td;
31436         Roo.Toolbar.Button.superclass.render.call(this, td);
31437     },
31438     
31439     /**
31440      * Removes and destroys this button
31441      */
31442     destroy : function(){
31443         Roo.Toolbar.Button.superclass.destroy.call(this);
31444         this.td.parentNode.removeChild(this.td);
31445     },
31446     
31447     /**
31448      * Shows this button
31449      */
31450     show: function(){
31451         this.hidden = false;
31452         this.td.style.display = "";
31453     },
31454     
31455     /**
31456      * Hides this button
31457      */
31458     hide: function(){
31459         this.hidden = true;
31460         this.td.style.display = "none";
31461     },
31462
31463     /**
31464      * Disables this item
31465      */
31466     disable : function(){
31467         Roo.fly(this.td).addClass("x-item-disabled");
31468         this.disabled = true;
31469     },
31470
31471     /**
31472      * Enables this item
31473      */
31474     enable : function(){
31475         Roo.fly(this.td).removeClass("x-item-disabled");
31476         this.disabled = false;
31477     }
31478 });
31479 // backwards compat
31480 Roo.ToolbarButton = Roo.Toolbar.Button;
31481
31482 /**
31483  * @class Roo.Toolbar.SplitButton
31484  * @extends Roo.SplitButton
31485  * A menu button that renders into a toolbar.
31486  * @constructor
31487  * Creates a new SplitButton
31488  * @param {Object} config A standard {@link Roo.SplitButton} config object
31489  */
31490 Roo.Toolbar.SplitButton = function(config){
31491     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31492 };
31493 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31494     render : function(td){
31495         this.td = td;
31496         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31497     },
31498     
31499     /**
31500      * Removes and destroys this button
31501      */
31502     destroy : function(){
31503         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31504         this.td.parentNode.removeChild(this.td);
31505     },
31506     
31507     /**
31508      * Shows this button
31509      */
31510     show: function(){
31511         this.hidden = false;
31512         this.td.style.display = "";
31513     },
31514     
31515     /**
31516      * Hides this button
31517      */
31518     hide: function(){
31519         this.hidden = true;
31520         this.td.style.display = "none";
31521     }
31522 });
31523
31524 // backwards compat
31525 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31526  * Based on:
31527  * Ext JS Library 1.1.1
31528  * Copyright(c) 2006-2007, Ext JS, LLC.
31529  *
31530  * Originally Released Under LGPL - original licence link has changed is not relivant.
31531  *
31532  * Fork - LGPL
31533  * <script type="text/javascript">
31534  */
31535  
31536 /**
31537  * @class Roo.PagingToolbar
31538  * @extends Roo.Toolbar
31539  * @children   Roo.Toolbar.Item Roo.form.Field
31540  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31541  * @constructor
31542  * Create a new PagingToolbar
31543  * @param {Object} config The config object
31544  */
31545 Roo.PagingToolbar = function(el, ds, config)
31546 {
31547     // old args format still supported... - xtype is prefered..
31548     if (typeof(el) == 'object' && el.xtype) {
31549         // created from xtype...
31550         config = el;
31551         ds = el.dataSource;
31552         el = config.container;
31553     }
31554     var items = [];
31555     if (config.items) {
31556         items = config.items;
31557         config.items = [];
31558     }
31559     
31560     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31561     this.ds = ds;
31562     this.cursor = 0;
31563     this.renderButtons(this.el);
31564     this.bind(ds);
31565     
31566     // supprot items array.
31567    
31568     Roo.each(items, function(e) {
31569         this.add(Roo.factory(e));
31570     },this);
31571     
31572 };
31573
31574 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31575    
31576     /**
31577      * @cfg {String/HTMLElement/Element} container
31578      * container The id or element that will contain the toolbar
31579      */
31580     /**
31581      * @cfg {Boolean} displayInfo
31582      * True to display the displayMsg (defaults to false)
31583      */
31584     
31585     
31586     /**
31587      * @cfg {Number} pageSize
31588      * The number of records to display per page (defaults to 20)
31589      */
31590     pageSize: 20,
31591     /**
31592      * @cfg {String} displayMsg
31593      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31594      */
31595     displayMsg : 'Displaying {0} - {1} of {2}',
31596     /**
31597      * @cfg {String} emptyMsg
31598      * The message to display when no records are found (defaults to "No data to display")
31599      */
31600     emptyMsg : 'No data to display',
31601     /**
31602      * Customizable piece of the default paging text (defaults to "Page")
31603      * @type String
31604      */
31605     beforePageText : "Page",
31606     /**
31607      * Customizable piece of the default paging text (defaults to "of %0")
31608      * @type String
31609      */
31610     afterPageText : "of {0}",
31611     /**
31612      * Customizable piece of the default paging text (defaults to "First Page")
31613      * @type String
31614      */
31615     firstText : "First Page",
31616     /**
31617      * Customizable piece of the default paging text (defaults to "Previous Page")
31618      * @type String
31619      */
31620     prevText : "Previous Page",
31621     /**
31622      * Customizable piece of the default paging text (defaults to "Next Page")
31623      * @type String
31624      */
31625     nextText : "Next Page",
31626     /**
31627      * Customizable piece of the default paging text (defaults to "Last Page")
31628      * @type String
31629      */
31630     lastText : "Last Page",
31631     /**
31632      * Customizable piece of the default paging text (defaults to "Refresh")
31633      * @type String
31634      */
31635     refreshText : "Refresh",
31636
31637     // private
31638     renderButtons : function(el){
31639         Roo.PagingToolbar.superclass.render.call(this, el);
31640         this.first = this.addButton({
31641             tooltip: this.firstText,
31642             cls: "x-btn-icon x-grid-page-first",
31643             disabled: true,
31644             handler: this.onClick.createDelegate(this, ["first"])
31645         });
31646         this.prev = this.addButton({
31647             tooltip: this.prevText,
31648             cls: "x-btn-icon x-grid-page-prev",
31649             disabled: true,
31650             handler: this.onClick.createDelegate(this, ["prev"])
31651         });
31652         //this.addSeparator();
31653         this.add(this.beforePageText);
31654         this.field = Roo.get(this.addDom({
31655            tag: "input",
31656            type: "text",
31657            size: "3",
31658            value: "1",
31659            cls: "x-grid-page-number"
31660         }).el);
31661         this.field.on("keydown", this.onPagingKeydown, this);
31662         this.field.on("focus", function(){this.dom.select();});
31663         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31664         this.field.setHeight(18);
31665         //this.addSeparator();
31666         this.next = this.addButton({
31667             tooltip: this.nextText,
31668             cls: "x-btn-icon x-grid-page-next",
31669             disabled: true,
31670             handler: this.onClick.createDelegate(this, ["next"])
31671         });
31672         this.last = this.addButton({
31673             tooltip: this.lastText,
31674             cls: "x-btn-icon x-grid-page-last",
31675             disabled: true,
31676             handler: this.onClick.createDelegate(this, ["last"])
31677         });
31678         //this.addSeparator();
31679         this.loading = this.addButton({
31680             tooltip: this.refreshText,
31681             cls: "x-btn-icon x-grid-loading",
31682             handler: this.onClick.createDelegate(this, ["refresh"])
31683         });
31684
31685         if(this.displayInfo){
31686             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31687         }
31688     },
31689
31690     // private
31691     updateInfo : function(){
31692         if(this.displayEl){
31693             var count = this.ds.getCount();
31694             var msg = count == 0 ?
31695                 this.emptyMsg :
31696                 String.format(
31697                     this.displayMsg,
31698                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31699                 );
31700             this.displayEl.update(msg);
31701         }
31702     },
31703
31704     // private
31705     onLoad : function(ds, r, o){
31706        this.cursor = o.params ? o.params.start : 0;
31707        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31708
31709        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31710        this.field.dom.value = ap;
31711        this.first.setDisabled(ap == 1);
31712        this.prev.setDisabled(ap == 1);
31713        this.next.setDisabled(ap == ps);
31714        this.last.setDisabled(ap == ps);
31715        this.loading.enable();
31716        this.updateInfo();
31717     },
31718
31719     // private
31720     getPageData : function(){
31721         var total = this.ds.getTotalCount();
31722         return {
31723             total : total,
31724             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31725             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31726         };
31727     },
31728
31729     // private
31730     onLoadError : function(){
31731         this.loading.enable();
31732     },
31733
31734     // private
31735     onPagingKeydown : function(e){
31736         var k = e.getKey();
31737         var d = this.getPageData();
31738         if(k == e.RETURN){
31739             var v = this.field.dom.value, pageNum;
31740             if(!v || isNaN(pageNum = parseInt(v, 10))){
31741                 this.field.dom.value = d.activePage;
31742                 return;
31743             }
31744             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31745             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31746             e.stopEvent();
31747         }
31748         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))
31749         {
31750           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31751           this.field.dom.value = pageNum;
31752           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31753           e.stopEvent();
31754         }
31755         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31756         {
31757           var v = this.field.dom.value, pageNum; 
31758           var increment = (e.shiftKey) ? 10 : 1;
31759           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31760             increment *= -1;
31761           }
31762           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31763             this.field.dom.value = d.activePage;
31764             return;
31765           }
31766           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31767           {
31768             this.field.dom.value = parseInt(v, 10) + increment;
31769             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31770             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31771           }
31772           e.stopEvent();
31773         }
31774     },
31775
31776     // private
31777     beforeLoad : function(){
31778         if(this.loading){
31779             this.loading.disable();
31780         }
31781     },
31782
31783     // private
31784     onClick : function(which){
31785         var ds = this.ds;
31786         switch(which){
31787             case "first":
31788                 ds.load({params:{start: 0, limit: this.pageSize}});
31789             break;
31790             case "prev":
31791                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31792             break;
31793             case "next":
31794                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31795             break;
31796             case "last":
31797                 var total = ds.getTotalCount();
31798                 var extra = total % this.pageSize;
31799                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31800                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31801             break;
31802             case "refresh":
31803                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31804             break;
31805         }
31806     },
31807
31808     /**
31809      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31810      * @param {Roo.data.Store} store The data store to unbind
31811      */
31812     unbind : function(ds){
31813         ds.un("beforeload", this.beforeLoad, this);
31814         ds.un("load", this.onLoad, this);
31815         ds.un("loadexception", this.onLoadError, this);
31816         ds.un("remove", this.updateInfo, this);
31817         ds.un("add", this.updateInfo, this);
31818         this.ds = undefined;
31819     },
31820
31821     /**
31822      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31823      * @param {Roo.data.Store} store The data store to bind
31824      */
31825     bind : function(ds){
31826         ds.on("beforeload", this.beforeLoad, this);
31827         ds.on("load", this.onLoad, this);
31828         ds.on("loadexception", this.onLoadError, this);
31829         ds.on("remove", this.updateInfo, this);
31830         ds.on("add", this.updateInfo, this);
31831         this.ds = ds;
31832     }
31833 });/*
31834  * Based on:
31835  * Ext JS Library 1.1.1
31836  * Copyright(c) 2006-2007, Ext JS, LLC.
31837  *
31838  * Originally Released Under LGPL - original licence link has changed is not relivant.
31839  *
31840  * Fork - LGPL
31841  * <script type="text/javascript">
31842  */
31843
31844 /**
31845  * @class Roo.Resizable
31846  * @extends Roo.util.Observable
31847  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31848  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31849  * 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
31850  * the element will be wrapped for you automatically.</p>
31851  * <p>Here is the list of valid resize handles:</p>
31852  * <pre>
31853 Value   Description
31854 ------  -------------------
31855  'n'     north
31856  's'     south
31857  'e'     east
31858  'w'     west
31859  'nw'    northwest
31860  'sw'    southwest
31861  'se'    southeast
31862  'ne'    northeast
31863  'hd'    horizontal drag
31864  'all'   all
31865 </pre>
31866  * <p>Here's an example showing the creation of a typical Resizable:</p>
31867  * <pre><code>
31868 var resizer = new Roo.Resizable("element-id", {
31869     handles: 'all',
31870     minWidth: 200,
31871     minHeight: 100,
31872     maxWidth: 500,
31873     maxHeight: 400,
31874     pinned: true
31875 });
31876 resizer.on("resize", myHandler);
31877 </code></pre>
31878  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31879  * resizer.east.setDisplayed(false);</p>
31880  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31881  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31882  * resize operation's new size (defaults to [0, 0])
31883  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31884  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31885  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31886  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31887  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31888  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31889  * @cfg {Number} width The width of the element in pixels (defaults to null)
31890  * @cfg {Number} height The height of the element in pixels (defaults to null)
31891  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31892  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31893  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31894  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31895  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31896  * in favor of the handles config option (defaults to false)
31897  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31898  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31899  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31900  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31901  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31902  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31903  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31904  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31905  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31906  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31907  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31908  * @constructor
31909  * Create a new resizable component
31910  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31911  * @param {Object} config configuration options
31912   */
31913 Roo.Resizable = function(el, config)
31914 {
31915     this.el = Roo.get(el);
31916
31917     if(config && config.wrap){
31918         config.resizeChild = this.el;
31919         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31920         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31921         this.el.setStyle("overflow", "hidden");
31922         this.el.setPositioning(config.resizeChild.getPositioning());
31923         config.resizeChild.clearPositioning();
31924         if(!config.width || !config.height){
31925             var csize = config.resizeChild.getSize();
31926             this.el.setSize(csize.width, csize.height);
31927         }
31928         if(config.pinned && !config.adjustments){
31929             config.adjustments = "auto";
31930         }
31931     }
31932
31933     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31934     this.proxy.unselectable();
31935     this.proxy.enableDisplayMode('block');
31936
31937     Roo.apply(this, config);
31938
31939     if(this.pinned){
31940         this.disableTrackOver = true;
31941         this.el.addClass("x-resizable-pinned");
31942     }
31943     // if the element isn't positioned, make it relative
31944     var position = this.el.getStyle("position");
31945     if(position != "absolute" && position != "fixed"){
31946         this.el.setStyle("position", "relative");
31947     }
31948     if(!this.handles){ // no handles passed, must be legacy style
31949         this.handles = 's,e,se';
31950         if(this.multiDirectional){
31951             this.handles += ',n,w';
31952         }
31953     }
31954     if(this.handles == "all"){
31955         this.handles = "n s e w ne nw se sw";
31956     }
31957     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31958     var ps = Roo.Resizable.positions;
31959     for(var i = 0, len = hs.length; i < len; i++){
31960         if(hs[i] && ps[hs[i]]){
31961             var pos = ps[hs[i]];
31962             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31963         }
31964     }
31965     // legacy
31966     this.corner = this.southeast;
31967     
31968     // updateBox = the box can move..
31969     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31970         this.updateBox = true;
31971     }
31972
31973     this.activeHandle = null;
31974
31975     if(this.resizeChild){
31976         if(typeof this.resizeChild == "boolean"){
31977             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31978         }else{
31979             this.resizeChild = Roo.get(this.resizeChild, true);
31980         }
31981     }
31982     
31983     if(this.adjustments == "auto"){
31984         var rc = this.resizeChild;
31985         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31986         if(rc && (hw || hn)){
31987             rc.position("relative");
31988             rc.setLeft(hw ? hw.el.getWidth() : 0);
31989             rc.setTop(hn ? hn.el.getHeight() : 0);
31990         }
31991         this.adjustments = [
31992             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31993             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31994         ];
31995     }
31996
31997     if(this.draggable){
31998         this.dd = this.dynamic ?
31999             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32000         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32001     }
32002
32003     // public events
32004     this.addEvents({
32005         /**
32006          * @event beforeresize
32007          * Fired before resize is allowed. Set enabled to false to cancel resize.
32008          * @param {Roo.Resizable} this
32009          * @param {Roo.EventObject} e The mousedown event
32010          */
32011         "beforeresize" : true,
32012         /**
32013          * @event resizing
32014          * Fired a resizing.
32015          * @param {Roo.Resizable} this
32016          * @param {Number} x The new x position
32017          * @param {Number} y The new y position
32018          * @param {Number} w The new w width
32019          * @param {Number} h The new h hight
32020          * @param {Roo.EventObject} e The mouseup event
32021          */
32022         "resizing" : true,
32023         /**
32024          * @event resize
32025          * Fired after a resize.
32026          * @param {Roo.Resizable} this
32027          * @param {Number} width The new width
32028          * @param {Number} height The new height
32029          * @param {Roo.EventObject} e The mouseup event
32030          */
32031         "resize" : true
32032     });
32033
32034     if(this.width !== null && this.height !== null){
32035         this.resizeTo(this.width, this.height);
32036     }else{
32037         this.updateChildSize();
32038     }
32039     if(Roo.isIE){
32040         this.el.dom.style.zoom = 1;
32041     }
32042     Roo.Resizable.superclass.constructor.call(this);
32043 };
32044
32045 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32046         resizeChild : false,
32047         adjustments : [0, 0],
32048         minWidth : 5,
32049         minHeight : 5,
32050         maxWidth : 10000,
32051         maxHeight : 10000,
32052         enabled : true,
32053         animate : false,
32054         duration : .35,
32055         dynamic : false,
32056         handles : false,
32057         multiDirectional : false,
32058         disableTrackOver : false,
32059         easing : 'easeOutStrong',
32060         widthIncrement : 0,
32061         heightIncrement : 0,
32062         pinned : false,
32063         width : null,
32064         height : null,
32065         preserveRatio : false,
32066         transparent: false,
32067         minX: 0,
32068         minY: 0,
32069         draggable: false,
32070
32071         /**
32072          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32073          */
32074         constrainTo: undefined,
32075         /**
32076          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32077          */
32078         resizeRegion: undefined,
32079
32080
32081     /**
32082      * Perform a manual resize
32083      * @param {Number} width
32084      * @param {Number} height
32085      */
32086     resizeTo : function(width, height){
32087         this.el.setSize(width, height);
32088         this.updateChildSize();
32089         this.fireEvent("resize", this, width, height, null);
32090     },
32091
32092     // private
32093     startSizing : function(e, handle){
32094         this.fireEvent("beforeresize", this, e);
32095         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32096
32097             if(!this.overlay){
32098                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32099                 this.overlay.unselectable();
32100                 this.overlay.enableDisplayMode("block");
32101                 this.overlay.on("mousemove", this.onMouseMove, this);
32102                 this.overlay.on("mouseup", this.onMouseUp, this);
32103             }
32104             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32105
32106             this.resizing = true;
32107             this.startBox = this.el.getBox();
32108             this.startPoint = e.getXY();
32109             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32110                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32111
32112             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32113             this.overlay.show();
32114
32115             if(this.constrainTo) {
32116                 var ct = Roo.get(this.constrainTo);
32117                 this.resizeRegion = ct.getRegion().adjust(
32118                     ct.getFrameWidth('t'),
32119                     ct.getFrameWidth('l'),
32120                     -ct.getFrameWidth('b'),
32121                     -ct.getFrameWidth('r')
32122                 );
32123             }
32124
32125             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32126             this.proxy.show();
32127             this.proxy.setBox(this.startBox);
32128             if(!this.dynamic){
32129                 this.proxy.setStyle('visibility', 'visible');
32130             }
32131         }
32132     },
32133
32134     // private
32135     onMouseDown : function(handle, e){
32136         if(this.enabled){
32137             e.stopEvent();
32138             this.activeHandle = handle;
32139             this.startSizing(e, handle);
32140         }
32141     },
32142
32143     // private
32144     onMouseUp : function(e){
32145         var size = this.resizeElement();
32146         this.resizing = false;
32147         this.handleOut();
32148         this.overlay.hide();
32149         this.proxy.hide();
32150         this.fireEvent("resize", this, size.width, size.height, e);
32151     },
32152
32153     // private
32154     updateChildSize : function(){
32155         
32156         if(this.resizeChild){
32157             var el = this.el;
32158             var child = this.resizeChild;
32159             var adj = this.adjustments;
32160             if(el.dom.offsetWidth){
32161                 var b = el.getSize(true);
32162                 child.setSize(b.width+adj[0], b.height+adj[1]);
32163             }
32164             // Second call here for IE
32165             // The first call enables instant resizing and
32166             // the second call corrects scroll bars if they
32167             // exist
32168             if(Roo.isIE){
32169                 setTimeout(function(){
32170                     if(el.dom.offsetWidth){
32171                         var b = el.getSize(true);
32172                         child.setSize(b.width+adj[0], b.height+adj[1]);
32173                     }
32174                 }, 10);
32175             }
32176         }
32177     },
32178
32179     // private
32180     snap : function(value, inc, min){
32181         if(!inc || !value) {
32182             return value;
32183         }
32184         var newValue = value;
32185         var m = value % inc;
32186         if(m > 0){
32187             if(m > (inc/2)){
32188                 newValue = value + (inc-m);
32189             }else{
32190                 newValue = value - m;
32191             }
32192         }
32193         return Math.max(min, newValue);
32194     },
32195
32196     // private
32197     resizeElement : function(){
32198         var box = this.proxy.getBox();
32199         if(this.updateBox){
32200             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32201         }else{
32202             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32203         }
32204         this.updateChildSize();
32205         if(!this.dynamic){
32206             this.proxy.hide();
32207         }
32208         return box;
32209     },
32210
32211     // private
32212     constrain : function(v, diff, m, mx){
32213         if(v - diff < m){
32214             diff = v - m;
32215         }else if(v - diff > mx){
32216             diff = mx - v;
32217         }
32218         return diff;
32219     },
32220
32221     // private
32222     onMouseMove : function(e){
32223         
32224         if(this.enabled){
32225             try{// try catch so if something goes wrong the user doesn't get hung
32226
32227             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32228                 return;
32229             }
32230
32231             //var curXY = this.startPoint;
32232             var curSize = this.curSize || this.startBox;
32233             var x = this.startBox.x, y = this.startBox.y;
32234             var ox = x, oy = y;
32235             var w = curSize.width, h = curSize.height;
32236             var ow = w, oh = h;
32237             var mw = this.minWidth, mh = this.minHeight;
32238             var mxw = this.maxWidth, mxh = this.maxHeight;
32239             var wi = this.widthIncrement;
32240             var hi = this.heightIncrement;
32241
32242             var eventXY = e.getXY();
32243             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32244             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32245
32246             var pos = this.activeHandle.position;
32247
32248             switch(pos){
32249                 case "east":
32250                     w += diffX;
32251                     w = Math.min(Math.max(mw, w), mxw);
32252                     break;
32253              
32254                 case "south":
32255                     h += diffY;
32256                     h = Math.min(Math.max(mh, h), mxh);
32257                     break;
32258                 case "southeast":
32259                     w += diffX;
32260                     h += diffY;
32261                     w = Math.min(Math.max(mw, w), mxw);
32262                     h = Math.min(Math.max(mh, h), mxh);
32263                     break;
32264                 case "north":
32265                     diffY = this.constrain(h, diffY, mh, mxh);
32266                     y += diffY;
32267                     h -= diffY;
32268                     break;
32269                 case "hdrag":
32270                     
32271                     if (wi) {
32272                         var adiffX = Math.abs(diffX);
32273                         var sub = (adiffX % wi); // how much 
32274                         if (sub > (wi/2)) { // far enough to snap
32275                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32276                         } else {
32277                             // remove difference.. 
32278                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32279                         }
32280                     }
32281                     x += diffX;
32282                     x = Math.max(this.minX, x);
32283                     break;
32284                 case "west":
32285                     diffX = this.constrain(w, diffX, mw, mxw);
32286                     x += diffX;
32287                     w -= diffX;
32288                     break;
32289                 case "northeast":
32290                     w += diffX;
32291                     w = Math.min(Math.max(mw, w), mxw);
32292                     diffY = this.constrain(h, diffY, mh, mxh);
32293                     y += diffY;
32294                     h -= diffY;
32295                     break;
32296                 case "northwest":
32297                     diffX = this.constrain(w, diffX, mw, mxw);
32298                     diffY = this.constrain(h, diffY, mh, mxh);
32299                     y += diffY;
32300                     h -= diffY;
32301                     x += diffX;
32302                     w -= diffX;
32303                     break;
32304                case "southwest":
32305                     diffX = this.constrain(w, diffX, mw, mxw);
32306                     h += diffY;
32307                     h = Math.min(Math.max(mh, h), mxh);
32308                     x += diffX;
32309                     w -= diffX;
32310                     break;
32311             }
32312
32313             var sw = this.snap(w, wi, mw);
32314             var sh = this.snap(h, hi, mh);
32315             if(sw != w || sh != h){
32316                 switch(pos){
32317                     case "northeast":
32318                         y -= sh - h;
32319                     break;
32320                     case "north":
32321                         y -= sh - h;
32322                         break;
32323                     case "southwest":
32324                         x -= sw - w;
32325                     break;
32326                     case "west":
32327                         x -= sw - w;
32328                         break;
32329                     case "northwest":
32330                         x -= sw - w;
32331                         y -= sh - h;
32332                     break;
32333                 }
32334                 w = sw;
32335                 h = sh;
32336             }
32337
32338             if(this.preserveRatio){
32339                 switch(pos){
32340                     case "southeast":
32341                     case "east":
32342                         h = oh * (w/ow);
32343                         h = Math.min(Math.max(mh, h), mxh);
32344                         w = ow * (h/oh);
32345                        break;
32346                     case "south":
32347                         w = ow * (h/oh);
32348                         w = Math.min(Math.max(mw, w), mxw);
32349                         h = oh * (w/ow);
32350                         break;
32351                     case "northeast":
32352                         w = ow * (h/oh);
32353                         w = Math.min(Math.max(mw, w), mxw);
32354                         h = oh * (w/ow);
32355                     break;
32356                     case "north":
32357                         var tw = w;
32358                         w = ow * (h/oh);
32359                         w = Math.min(Math.max(mw, w), mxw);
32360                         h = oh * (w/ow);
32361                         x += (tw - w) / 2;
32362                         break;
32363                     case "southwest":
32364                         h = oh * (w/ow);
32365                         h = Math.min(Math.max(mh, h), mxh);
32366                         var tw = w;
32367                         w = ow * (h/oh);
32368                         x += tw - w;
32369                         break;
32370                     case "west":
32371                         var th = h;
32372                         h = oh * (w/ow);
32373                         h = Math.min(Math.max(mh, h), mxh);
32374                         y += (th - h) / 2;
32375                         var tw = w;
32376                         w = ow * (h/oh);
32377                         x += tw - w;
32378                        break;
32379                     case "northwest":
32380                         var tw = w;
32381                         var th = h;
32382                         h = oh * (w/ow);
32383                         h = Math.min(Math.max(mh, h), mxh);
32384                         w = ow * (h/oh);
32385                         y += th - h;
32386                         x += tw - w;
32387                        break;
32388
32389                 }
32390             }
32391             if (pos == 'hdrag') {
32392                 w = ow;
32393             }
32394             this.proxy.setBounds(x, y, w, h);
32395             if(this.dynamic){
32396                 this.resizeElement();
32397             }
32398             }catch(e){}
32399         }
32400         this.fireEvent("resizing", this, x, y, w, h, e);
32401     },
32402
32403     // private
32404     handleOver : function(){
32405         if(this.enabled){
32406             this.el.addClass("x-resizable-over");
32407         }
32408     },
32409
32410     // private
32411     handleOut : function(){
32412         if(!this.resizing){
32413             this.el.removeClass("x-resizable-over");
32414         }
32415     },
32416
32417     /**
32418      * Returns the element this component is bound to.
32419      * @return {Roo.Element}
32420      */
32421     getEl : function(){
32422         return this.el;
32423     },
32424
32425     /**
32426      * Returns the resizeChild element (or null).
32427      * @return {Roo.Element}
32428      */
32429     getResizeChild : function(){
32430         return this.resizeChild;
32431     },
32432     groupHandler : function()
32433     {
32434         
32435     },
32436     /**
32437      * Destroys this resizable. If the element was wrapped and
32438      * removeEl is not true then the element remains.
32439      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32440      */
32441     destroy : function(removeEl){
32442         this.proxy.remove();
32443         if(this.overlay){
32444             this.overlay.removeAllListeners();
32445             this.overlay.remove();
32446         }
32447         var ps = Roo.Resizable.positions;
32448         for(var k in ps){
32449             if(typeof ps[k] != "function" && this[ps[k]]){
32450                 var h = this[ps[k]];
32451                 h.el.removeAllListeners();
32452                 h.el.remove();
32453             }
32454         }
32455         if(removeEl){
32456             this.el.update("");
32457             this.el.remove();
32458         }
32459     }
32460 });
32461
32462 // private
32463 // hash to map config positions to true positions
32464 Roo.Resizable.positions = {
32465     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32466     hd: "hdrag"
32467 };
32468
32469 // private
32470 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32471     if(!this.tpl){
32472         // only initialize the template if resizable is used
32473         var tpl = Roo.DomHelper.createTemplate(
32474             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32475         );
32476         tpl.compile();
32477         Roo.Resizable.Handle.prototype.tpl = tpl;
32478     }
32479     this.position = pos;
32480     this.rz = rz;
32481     // show north drag fro topdra
32482     var handlepos = pos == 'hdrag' ? 'north' : pos;
32483     
32484     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32485     if (pos == 'hdrag') {
32486         this.el.setStyle('cursor', 'pointer');
32487     }
32488     this.el.unselectable();
32489     if(transparent){
32490         this.el.setOpacity(0);
32491     }
32492     this.el.on("mousedown", this.onMouseDown, this);
32493     if(!disableTrackOver){
32494         this.el.on("mouseover", this.onMouseOver, this);
32495         this.el.on("mouseout", this.onMouseOut, this);
32496     }
32497 };
32498
32499 // private
32500 Roo.Resizable.Handle.prototype = {
32501     afterResize : function(rz){
32502         Roo.log('after?');
32503         // do nothing
32504     },
32505     // private
32506     onMouseDown : function(e){
32507         this.rz.onMouseDown(this, e);
32508     },
32509     // private
32510     onMouseOver : function(e){
32511         this.rz.handleOver(this, e);
32512     },
32513     // private
32514     onMouseOut : function(e){
32515         this.rz.handleOut(this, e);
32516     }
32517 };/*
32518  * Based on:
32519  * Ext JS Library 1.1.1
32520  * Copyright(c) 2006-2007, Ext JS, LLC.
32521  *
32522  * Originally Released Under LGPL - original licence link has changed is not relivant.
32523  *
32524  * Fork - LGPL
32525  * <script type="text/javascript">
32526  */
32527
32528 /**
32529  * @class Roo.Editor
32530  * @extends Roo.Component
32531  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32532  * @constructor
32533  * Create a new Editor
32534  * @param {Roo.form.Field} field The Field object (or descendant)
32535  * @param {Object} config The config object
32536  */
32537 Roo.Editor = function(field, config){
32538     Roo.Editor.superclass.constructor.call(this, config);
32539     this.field = field;
32540     this.addEvents({
32541         /**
32542              * @event beforestartedit
32543              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32544              * false from the handler of this event.
32545              * @param {Editor} this
32546              * @param {Roo.Element} boundEl The underlying element bound to this editor
32547              * @param {Mixed} value The field value being set
32548              */
32549         "beforestartedit" : true,
32550         /**
32551              * @event startedit
32552              * Fires when this editor is displayed
32553              * @param {Roo.Element} boundEl The underlying element bound to this editor
32554              * @param {Mixed} value The starting field value
32555              */
32556         "startedit" : true,
32557         /**
32558              * @event beforecomplete
32559              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32560              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32561              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32562              * event will not fire since no edit actually occurred.
32563              * @param {Editor} this
32564              * @param {Mixed} value The current field value
32565              * @param {Mixed} startValue The original field value
32566              */
32567         "beforecomplete" : true,
32568         /**
32569              * @event complete
32570              * Fires after editing is complete and any changed value has been written to the underlying field.
32571              * @param {Editor} this
32572              * @param {Mixed} value The current field value
32573              * @param {Mixed} startValue The original field value
32574              */
32575         "complete" : true,
32576         /**
32577          * @event specialkey
32578          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32579          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32580          * @param {Roo.form.Field} this
32581          * @param {Roo.EventObject} e The event object
32582          */
32583         "specialkey" : true
32584     });
32585 };
32586
32587 Roo.extend(Roo.Editor, Roo.Component, {
32588     /**
32589      * @cfg {Boolean/String} autosize
32590      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32591      * or "height" to adopt the height only (defaults to false)
32592      */
32593     /**
32594      * @cfg {Boolean} revertInvalid
32595      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32596      * validation fails (defaults to true)
32597      */
32598     /**
32599      * @cfg {Boolean} ignoreNoChange
32600      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32601      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32602      * will never be ignored.
32603      */
32604     /**
32605      * @cfg {Boolean} hideEl
32606      * False to keep the bound element visible while the editor is displayed (defaults to true)
32607      */
32608     /**
32609      * @cfg {Mixed} value
32610      * The data value of the underlying field (defaults to "")
32611      */
32612     value : "",
32613     /**
32614      * @cfg {String} alignment
32615      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32616      */
32617     alignment: "c-c?",
32618     /**
32619      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32620      * for bottom-right shadow (defaults to "frame")
32621      */
32622     shadow : "frame",
32623     /**
32624      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32625      */
32626     constrain : false,
32627     /**
32628      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32629      */
32630     completeOnEnter : false,
32631     /**
32632      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32633      */
32634     cancelOnEsc : false,
32635     /**
32636      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32637      */
32638     updateEl : false,
32639
32640     // private
32641     onRender : function(ct, position){
32642         this.el = new Roo.Layer({
32643             shadow: this.shadow,
32644             cls: "x-editor",
32645             parentEl : ct,
32646             shim : this.shim,
32647             shadowOffset:4,
32648             id: this.id,
32649             constrain: this.constrain
32650         });
32651         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32652         if(this.field.msgTarget != 'title'){
32653             this.field.msgTarget = 'qtip';
32654         }
32655         this.field.render(this.el);
32656         if(Roo.isGecko){
32657             this.field.el.dom.setAttribute('autocomplete', 'off');
32658         }
32659         this.field.on("specialkey", this.onSpecialKey, this);
32660         if(this.swallowKeys){
32661             this.field.el.swallowEvent(['keydown','keypress']);
32662         }
32663         this.field.show();
32664         this.field.on("blur", this.onBlur, this);
32665         if(this.field.grow){
32666             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32667         }
32668     },
32669
32670     onSpecialKey : function(field, e)
32671     {
32672         //Roo.log('editor onSpecialKey');
32673         if(this.completeOnEnter && e.getKey() == e.ENTER){
32674             e.stopEvent();
32675             this.completeEdit();
32676             return;
32677         }
32678         // do not fire special key otherwise it might hide close the editor...
32679         if(e.getKey() == e.ENTER){    
32680             return;
32681         }
32682         if(this.cancelOnEsc && e.getKey() == e.ESC){
32683             this.cancelEdit();
32684             return;
32685         } 
32686         this.fireEvent('specialkey', field, e);
32687     
32688     },
32689
32690     /**
32691      * Starts the editing process and shows the editor.
32692      * @param {String/HTMLElement/Element} el The element to edit
32693      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32694       * to the innerHTML of el.
32695      */
32696     startEdit : function(el, value){
32697         if(this.editing){
32698             this.completeEdit();
32699         }
32700         this.boundEl = Roo.get(el);
32701         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32702         if(!this.rendered){
32703             this.render(this.parentEl || document.body);
32704         }
32705         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32706             return;
32707         }
32708         this.startValue = v;
32709         this.field.setValue(v);
32710         if(this.autoSize){
32711             var sz = this.boundEl.getSize();
32712             switch(this.autoSize){
32713                 case "width":
32714                 this.setSize(sz.width,  "");
32715                 break;
32716                 case "height":
32717                 this.setSize("",  sz.height);
32718                 break;
32719                 default:
32720                 this.setSize(sz.width,  sz.height);
32721             }
32722         }
32723         this.el.alignTo(this.boundEl, this.alignment);
32724         this.editing = true;
32725         if(Roo.QuickTips){
32726             Roo.QuickTips.disable();
32727         }
32728         this.show();
32729     },
32730
32731     /**
32732      * Sets the height and width of this editor.
32733      * @param {Number} width The new width
32734      * @param {Number} height The new height
32735      */
32736     setSize : function(w, h){
32737         this.field.setSize(w, h);
32738         if(this.el){
32739             this.el.sync();
32740         }
32741     },
32742
32743     /**
32744      * Realigns the editor to the bound field based on the current alignment config value.
32745      */
32746     realign : function(){
32747         this.el.alignTo(this.boundEl, this.alignment);
32748     },
32749
32750     /**
32751      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32752      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32753      */
32754     completeEdit : function(remainVisible){
32755         if(!this.editing){
32756             return;
32757         }
32758         var v = this.getValue();
32759         if(this.revertInvalid !== false && !this.field.isValid()){
32760             v = this.startValue;
32761             this.cancelEdit(true);
32762         }
32763         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32764             this.editing = false;
32765             this.hide();
32766             return;
32767         }
32768         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32769             this.editing = false;
32770             if(this.updateEl && this.boundEl){
32771                 this.boundEl.update(v);
32772             }
32773             if(remainVisible !== true){
32774                 this.hide();
32775             }
32776             this.fireEvent("complete", this, v, this.startValue);
32777         }
32778     },
32779
32780     // private
32781     onShow : function(){
32782         this.el.show();
32783         if(this.hideEl !== false){
32784             this.boundEl.hide();
32785         }
32786         this.field.show();
32787         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32788             this.fixIEFocus = true;
32789             this.deferredFocus.defer(50, this);
32790         }else{
32791             this.field.focus();
32792         }
32793         this.fireEvent("startedit", this.boundEl, this.startValue);
32794     },
32795
32796     deferredFocus : function(){
32797         if(this.editing){
32798             this.field.focus();
32799         }
32800     },
32801
32802     /**
32803      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32804      * reverted to the original starting value.
32805      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32806      * cancel (defaults to false)
32807      */
32808     cancelEdit : function(remainVisible){
32809         if(this.editing){
32810             this.setValue(this.startValue);
32811             if(remainVisible !== true){
32812                 this.hide();
32813             }
32814         }
32815     },
32816
32817     // private
32818     onBlur : function(){
32819         if(this.allowBlur !== true && this.editing){
32820             this.completeEdit();
32821         }
32822     },
32823
32824     // private
32825     onHide : function(){
32826         if(this.editing){
32827             this.completeEdit();
32828             return;
32829         }
32830         this.field.blur();
32831         if(this.field.collapse){
32832             this.field.collapse();
32833         }
32834         this.el.hide();
32835         if(this.hideEl !== false){
32836             this.boundEl.show();
32837         }
32838         if(Roo.QuickTips){
32839             Roo.QuickTips.enable();
32840         }
32841     },
32842
32843     /**
32844      * Sets the data value of the editor
32845      * @param {Mixed} value Any valid value supported by the underlying field
32846      */
32847     setValue : function(v){
32848         this.field.setValue(v);
32849     },
32850
32851     /**
32852      * Gets the data value of the editor
32853      * @return {Mixed} The data value
32854      */
32855     getValue : function(){
32856         return this.field.getValue();
32857     }
32858 });/*
32859  * Based on:
32860  * Ext JS Library 1.1.1
32861  * Copyright(c) 2006-2007, Ext JS, LLC.
32862  *
32863  * Originally Released Under LGPL - original licence link has changed is not relivant.
32864  *
32865  * Fork - LGPL
32866  * <script type="text/javascript">
32867  */
32868  
32869 /**
32870  * @class Roo.BasicDialog
32871  * @extends Roo.util.Observable
32872  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32873  * <pre><code>
32874 var dlg = new Roo.BasicDialog("my-dlg", {
32875     height: 200,
32876     width: 300,
32877     minHeight: 100,
32878     minWidth: 150,
32879     modal: true,
32880     proxyDrag: true,
32881     shadow: true
32882 });
32883 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32884 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32885 dlg.addButton('Cancel', dlg.hide, dlg);
32886 dlg.show();
32887 </code></pre>
32888   <b>A Dialog should always be a direct child of the body element.</b>
32889  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32890  * @cfg {String} title Default text to display in the title bar (defaults to null)
32891  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32892  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32893  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32894  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32895  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32896  * (defaults to null with no animation)
32897  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32898  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32899  * property for valid values (defaults to 'all')
32900  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32901  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32902  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32903  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32904  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32905  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32906  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32907  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32908  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32909  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32910  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32911  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32912  * draggable = true (defaults to false)
32913  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32914  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32915  * shadow (defaults to false)
32916  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32917  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32918  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32919  * @cfg {Array} buttons Array of buttons
32920  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32921  * @constructor
32922  * Create a new BasicDialog.
32923  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32924  * @param {Object} config Configuration options
32925  */
32926 Roo.BasicDialog = function(el, config){
32927     this.el = Roo.get(el);
32928     var dh = Roo.DomHelper;
32929     if(!this.el && config && config.autoCreate){
32930         if(typeof config.autoCreate == "object"){
32931             if(!config.autoCreate.id){
32932                 config.autoCreate.id = el;
32933             }
32934             this.el = dh.append(document.body,
32935                         config.autoCreate, true);
32936         }else{
32937             this.el = dh.append(document.body,
32938                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32939         }
32940     }
32941     el = this.el;
32942     el.setDisplayed(true);
32943     el.hide = this.hideAction;
32944     this.id = el.id;
32945     el.addClass("x-dlg");
32946
32947     Roo.apply(this, config);
32948
32949     this.proxy = el.createProxy("x-dlg-proxy");
32950     this.proxy.hide = this.hideAction;
32951     this.proxy.setOpacity(.5);
32952     this.proxy.hide();
32953
32954     if(config.width){
32955         el.setWidth(config.width);
32956     }
32957     if(config.height){
32958         el.setHeight(config.height);
32959     }
32960     this.size = el.getSize();
32961     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32962         this.xy = [config.x,config.y];
32963     }else{
32964         this.xy = el.getCenterXY(true);
32965     }
32966     /** The header element @type Roo.Element */
32967     this.header = el.child("> .x-dlg-hd");
32968     /** The body element @type Roo.Element */
32969     this.body = el.child("> .x-dlg-bd");
32970     /** The footer element @type Roo.Element */
32971     this.footer = el.child("> .x-dlg-ft");
32972
32973     if(!this.header){
32974         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32975     }
32976     if(!this.body){
32977         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32978     }
32979
32980     this.header.unselectable();
32981     if(this.title){
32982         this.header.update(this.title);
32983     }
32984     // this element allows the dialog to be focused for keyboard event
32985     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32986     this.focusEl.swallowEvent("click", true);
32987
32988     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32989
32990     // wrap the body and footer for special rendering
32991     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32992     if(this.footer){
32993         this.bwrap.dom.appendChild(this.footer.dom);
32994     }
32995
32996     this.bg = this.el.createChild({
32997         tag: "div", cls:"x-dlg-bg",
32998         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32999     });
33000     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33001
33002
33003     if(this.autoScroll !== false && !this.autoTabs){
33004         this.body.setStyle("overflow", "auto");
33005     }
33006
33007     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33008
33009     if(this.closable !== false){
33010         this.el.addClass("x-dlg-closable");
33011         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33012         this.close.on("click", this.closeClick, this);
33013         this.close.addClassOnOver("x-dlg-close-over");
33014     }
33015     if(this.collapsible !== false){
33016         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33017         this.collapseBtn.on("click", this.collapseClick, this);
33018         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33019         this.header.on("dblclick", this.collapseClick, this);
33020     }
33021     if(this.resizable !== false){
33022         this.el.addClass("x-dlg-resizable");
33023         this.resizer = new Roo.Resizable(el, {
33024             minWidth: this.minWidth || 80,
33025             minHeight:this.minHeight || 80,
33026             handles: this.resizeHandles || "all",
33027             pinned: true
33028         });
33029         this.resizer.on("beforeresize", this.beforeResize, this);
33030         this.resizer.on("resize", this.onResize, this);
33031     }
33032     if(this.draggable !== false){
33033         el.addClass("x-dlg-draggable");
33034         if (!this.proxyDrag) {
33035             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33036         }
33037         else {
33038             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33039         }
33040         dd.setHandleElId(this.header.id);
33041         dd.endDrag = this.endMove.createDelegate(this);
33042         dd.startDrag = this.startMove.createDelegate(this);
33043         dd.onDrag = this.onDrag.createDelegate(this);
33044         dd.scroll = false;
33045         this.dd = dd;
33046     }
33047     if(this.modal){
33048         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33049         this.mask.enableDisplayMode("block");
33050         this.mask.hide();
33051         this.el.addClass("x-dlg-modal");
33052     }
33053     if(this.shadow){
33054         this.shadow = new Roo.Shadow({
33055             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33056             offset : this.shadowOffset
33057         });
33058     }else{
33059         this.shadowOffset = 0;
33060     }
33061     if(Roo.useShims && this.shim !== false){
33062         this.shim = this.el.createShim();
33063         this.shim.hide = this.hideAction;
33064         this.shim.hide();
33065     }else{
33066         this.shim = false;
33067     }
33068     if(this.autoTabs){
33069         this.initTabs();
33070     }
33071     if (this.buttons) { 
33072         var bts= this.buttons;
33073         this.buttons = [];
33074         Roo.each(bts, function(b) {
33075             this.addButton(b);
33076         }, this);
33077     }
33078     
33079     
33080     this.addEvents({
33081         /**
33082          * @event keydown
33083          * Fires when a key is pressed
33084          * @param {Roo.BasicDialog} this
33085          * @param {Roo.EventObject} e
33086          */
33087         "keydown" : true,
33088         /**
33089          * @event move
33090          * Fires when this dialog is moved by the user.
33091          * @param {Roo.BasicDialog} this
33092          * @param {Number} x The new page X
33093          * @param {Number} y The new page Y
33094          */
33095         "move" : true,
33096         /**
33097          * @event resize
33098          * Fires when this dialog is resized by the user.
33099          * @param {Roo.BasicDialog} this
33100          * @param {Number} width The new width
33101          * @param {Number} height The new height
33102          */
33103         "resize" : true,
33104         /**
33105          * @event beforehide
33106          * Fires before this dialog is hidden.
33107          * @param {Roo.BasicDialog} this
33108          */
33109         "beforehide" : true,
33110         /**
33111          * @event hide
33112          * Fires when this dialog is hidden.
33113          * @param {Roo.BasicDialog} this
33114          */
33115         "hide" : true,
33116         /**
33117          * @event beforeshow
33118          * Fires before this dialog is shown.
33119          * @param {Roo.BasicDialog} this
33120          */
33121         "beforeshow" : true,
33122         /**
33123          * @event show
33124          * Fires when this dialog is shown.
33125          * @param {Roo.BasicDialog} this
33126          */
33127         "show" : true
33128     });
33129     el.on("keydown", this.onKeyDown, this);
33130     el.on("mousedown", this.toFront, this);
33131     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33132     this.el.hide();
33133     Roo.DialogManager.register(this);
33134     Roo.BasicDialog.superclass.constructor.call(this);
33135 };
33136
33137 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33138     shadowOffset: Roo.isIE ? 6 : 5,
33139     minHeight: 80,
33140     minWidth: 200,
33141     minButtonWidth: 75,
33142     defaultButton: null,
33143     buttonAlign: "right",
33144     tabTag: 'div',
33145     firstShow: true,
33146
33147     /**
33148      * Sets the dialog title text
33149      * @param {String} text The title text to display
33150      * @return {Roo.BasicDialog} this
33151      */
33152     setTitle : function(text){
33153         this.header.update(text);
33154         return this;
33155     },
33156
33157     // private
33158     closeClick : function(){
33159         this.hide();
33160     },
33161
33162     // private
33163     collapseClick : function(){
33164         this[this.collapsed ? "expand" : "collapse"]();
33165     },
33166
33167     /**
33168      * Collapses the dialog to its minimized state (only the title bar is visible).
33169      * Equivalent to the user clicking the collapse dialog button.
33170      */
33171     collapse : function(){
33172         if(!this.collapsed){
33173             this.collapsed = true;
33174             this.el.addClass("x-dlg-collapsed");
33175             this.restoreHeight = this.el.getHeight();
33176             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33177         }
33178     },
33179
33180     /**
33181      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33182      * clicking the expand dialog button.
33183      */
33184     expand : function(){
33185         if(this.collapsed){
33186             this.collapsed = false;
33187             this.el.removeClass("x-dlg-collapsed");
33188             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33189         }
33190     },
33191
33192     /**
33193      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33194      * @return {Roo.TabPanel} The tabs component
33195      */
33196     initTabs : function(){
33197         var tabs = this.getTabs();
33198         while(tabs.getTab(0)){
33199             tabs.removeTab(0);
33200         }
33201         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33202             var dom = el.dom;
33203             tabs.addTab(Roo.id(dom), dom.title);
33204             dom.title = "";
33205         });
33206         tabs.activate(0);
33207         return tabs;
33208     },
33209
33210     // private
33211     beforeResize : function(){
33212         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33213     },
33214
33215     // private
33216     onResize : function(){
33217         this.refreshSize();
33218         this.syncBodyHeight();
33219         this.adjustAssets();
33220         this.focus();
33221         this.fireEvent("resize", this, this.size.width, this.size.height);
33222     },
33223
33224     // private
33225     onKeyDown : function(e){
33226         if(this.isVisible()){
33227             this.fireEvent("keydown", this, e);
33228         }
33229     },
33230
33231     /**
33232      * Resizes the dialog.
33233      * @param {Number} width
33234      * @param {Number} height
33235      * @return {Roo.BasicDialog} this
33236      */
33237     resizeTo : function(width, height){
33238         this.el.setSize(width, height);
33239         this.size = {width: width, height: height};
33240         this.syncBodyHeight();
33241         if(this.fixedcenter){
33242             this.center();
33243         }
33244         if(this.isVisible()){
33245             this.constrainXY();
33246             this.adjustAssets();
33247         }
33248         this.fireEvent("resize", this, width, height);
33249         return this;
33250     },
33251
33252
33253     /**
33254      * Resizes the dialog to fit the specified content size.
33255      * @param {Number} width
33256      * @param {Number} height
33257      * @return {Roo.BasicDialog} this
33258      */
33259     setContentSize : function(w, h){
33260         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33261         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33262         //if(!this.el.isBorderBox()){
33263             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33264             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33265         //}
33266         if(this.tabs){
33267             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33268             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33269         }
33270         this.resizeTo(w, h);
33271         return this;
33272     },
33273
33274     /**
33275      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33276      * executed in response to a particular key being pressed while the dialog is active.
33277      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33278      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33279      * @param {Function} fn The function to call
33280      * @param {Object} scope (optional) The scope of the function
33281      * @return {Roo.BasicDialog} this
33282      */
33283     addKeyListener : function(key, fn, scope){
33284         var keyCode, shift, ctrl, alt;
33285         if(typeof key == "object" && !(key instanceof Array)){
33286             keyCode = key["key"];
33287             shift = key["shift"];
33288             ctrl = key["ctrl"];
33289             alt = key["alt"];
33290         }else{
33291             keyCode = key;
33292         }
33293         var handler = function(dlg, e){
33294             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33295                 var k = e.getKey();
33296                 if(keyCode instanceof Array){
33297                     for(var i = 0, len = keyCode.length; i < len; i++){
33298                         if(keyCode[i] == k){
33299                           fn.call(scope || window, dlg, k, e);
33300                           return;
33301                         }
33302                     }
33303                 }else{
33304                     if(k == keyCode){
33305                         fn.call(scope || window, dlg, k, e);
33306                     }
33307                 }
33308             }
33309         };
33310         this.on("keydown", handler);
33311         return this;
33312     },
33313
33314     /**
33315      * Returns the TabPanel component (creates it if it doesn't exist).
33316      * Note: If you wish to simply check for the existence of tabs without creating them,
33317      * check for a null 'tabs' property.
33318      * @return {Roo.TabPanel} The tabs component
33319      */
33320     getTabs : function(){
33321         if(!this.tabs){
33322             this.el.addClass("x-dlg-auto-tabs");
33323             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33324             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33325         }
33326         return this.tabs;
33327     },
33328
33329     /**
33330      * Adds a button to the footer section of the dialog.
33331      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33332      * object or a valid Roo.DomHelper element config
33333      * @param {Function} handler The function called when the button is clicked
33334      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33335      * @return {Roo.Button} The new button
33336      */
33337     addButton : function(config, handler, scope){
33338         var dh = Roo.DomHelper;
33339         if(!this.footer){
33340             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33341         }
33342         if(!this.btnContainer){
33343             var tb = this.footer.createChild({
33344
33345                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33346                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33347             }, null, true);
33348             this.btnContainer = tb.firstChild.firstChild.firstChild;
33349         }
33350         var bconfig = {
33351             handler: handler,
33352             scope: scope,
33353             minWidth: this.minButtonWidth,
33354             hideParent:true
33355         };
33356         if(typeof config == "string"){
33357             bconfig.text = config;
33358         }else{
33359             if(config.tag){
33360                 bconfig.dhconfig = config;
33361             }else{
33362                 Roo.apply(bconfig, config);
33363             }
33364         }
33365         var fc = false;
33366         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33367             bconfig.position = Math.max(0, bconfig.position);
33368             fc = this.btnContainer.childNodes[bconfig.position];
33369         }
33370          
33371         var btn = new Roo.Button(
33372             fc ? 
33373                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33374                 : this.btnContainer.appendChild(document.createElement("td")),
33375             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33376             bconfig
33377         );
33378         this.syncBodyHeight();
33379         if(!this.buttons){
33380             /**
33381              * Array of all the buttons that have been added to this dialog via addButton
33382              * @type Array
33383              */
33384             this.buttons = [];
33385         }
33386         this.buttons.push(btn);
33387         return btn;
33388     },
33389
33390     /**
33391      * Sets the default button to be focused when the dialog is displayed.
33392      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33393      * @return {Roo.BasicDialog} this
33394      */
33395     setDefaultButton : function(btn){
33396         this.defaultButton = btn;
33397         return this;
33398     },
33399
33400     // private
33401     getHeaderFooterHeight : function(safe){
33402         var height = 0;
33403         if(this.header){
33404            height += this.header.getHeight();
33405         }
33406         if(this.footer){
33407            var fm = this.footer.getMargins();
33408             height += (this.footer.getHeight()+fm.top+fm.bottom);
33409         }
33410         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33411         height += this.centerBg.getPadding("tb");
33412         return height;
33413     },
33414
33415     // private
33416     syncBodyHeight : function()
33417     {
33418         var bd = this.body, // the text
33419             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33420             bw = this.bwrap;
33421         var height = this.size.height - this.getHeaderFooterHeight(false);
33422         bd.setHeight(height-bd.getMargins("tb"));
33423         var hh = this.header.getHeight();
33424         var h = this.size.height-hh;
33425         cb.setHeight(h);
33426         
33427         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33428         bw.setHeight(h-cb.getPadding("tb"));
33429         
33430         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33431         bd.setWidth(bw.getWidth(true));
33432         if(this.tabs){
33433             this.tabs.syncHeight();
33434             if(Roo.isIE){
33435                 this.tabs.el.repaint();
33436             }
33437         }
33438     },
33439
33440     /**
33441      * Restores the previous state of the dialog if Roo.state is configured.
33442      * @return {Roo.BasicDialog} this
33443      */
33444     restoreState : function(){
33445         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33446         if(box && box.width){
33447             this.xy = [box.x, box.y];
33448             this.resizeTo(box.width, box.height);
33449         }
33450         return this;
33451     },
33452
33453     // private
33454     beforeShow : function(){
33455         this.expand();
33456         if(this.fixedcenter){
33457             this.xy = this.el.getCenterXY(true);
33458         }
33459         if(this.modal){
33460             Roo.get(document.body).addClass("x-body-masked");
33461             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33462             this.mask.show();
33463         }
33464         this.constrainXY();
33465     },
33466
33467     // private
33468     animShow : function(){
33469         var b = Roo.get(this.animateTarget).getBox();
33470         this.proxy.setSize(b.width, b.height);
33471         this.proxy.setLocation(b.x, b.y);
33472         this.proxy.show();
33473         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33474                     true, .35, this.showEl.createDelegate(this));
33475     },
33476
33477     /**
33478      * Shows the dialog.
33479      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33480      * @return {Roo.BasicDialog} this
33481      */
33482     show : function(animateTarget){
33483         if (this.fireEvent("beforeshow", this) === false){
33484             return;
33485         }
33486         if(this.syncHeightBeforeShow){
33487             this.syncBodyHeight();
33488         }else if(this.firstShow){
33489             this.firstShow = false;
33490             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33491         }
33492         this.animateTarget = animateTarget || this.animateTarget;
33493         if(!this.el.isVisible()){
33494             this.beforeShow();
33495             if(this.animateTarget && Roo.get(this.animateTarget)){
33496                 this.animShow();
33497             }else{
33498                 this.showEl();
33499             }
33500         }
33501         return this;
33502     },
33503
33504     // private
33505     showEl : function(){
33506         this.proxy.hide();
33507         this.el.setXY(this.xy);
33508         this.el.show();
33509         this.adjustAssets(true);
33510         this.toFront();
33511         this.focus();
33512         // IE peekaboo bug - fix found by Dave Fenwick
33513         if(Roo.isIE){
33514             this.el.repaint();
33515         }
33516         this.fireEvent("show", this);
33517     },
33518
33519     /**
33520      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33521      * dialog itself will receive focus.
33522      */
33523     focus : function(){
33524         if(this.defaultButton){
33525             this.defaultButton.focus();
33526         }else{
33527             this.focusEl.focus();
33528         }
33529     },
33530
33531     // private
33532     constrainXY : function(){
33533         if(this.constraintoviewport !== false){
33534             if(!this.viewSize){
33535                 if(this.container){
33536                     var s = this.container.getSize();
33537                     this.viewSize = [s.width, s.height];
33538                 }else{
33539                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33540                 }
33541             }
33542             var s = Roo.get(this.container||document).getScroll();
33543
33544             var x = this.xy[0], y = this.xy[1];
33545             var w = this.size.width, h = this.size.height;
33546             var vw = this.viewSize[0], vh = this.viewSize[1];
33547             // only move it if it needs it
33548             var moved = false;
33549             // first validate right/bottom
33550             if(x + w > vw+s.left){
33551                 x = vw - w;
33552                 moved = true;
33553             }
33554             if(y + h > vh+s.top){
33555                 y = vh - h;
33556                 moved = true;
33557             }
33558             // then make sure top/left isn't negative
33559             if(x < s.left){
33560                 x = s.left;
33561                 moved = true;
33562             }
33563             if(y < s.top){
33564                 y = s.top;
33565                 moved = true;
33566             }
33567             if(moved){
33568                 // cache xy
33569                 this.xy = [x, y];
33570                 if(this.isVisible()){
33571                     this.el.setLocation(x, y);
33572                     this.adjustAssets();
33573                 }
33574             }
33575         }
33576     },
33577
33578     // private
33579     onDrag : function(){
33580         if(!this.proxyDrag){
33581             this.xy = this.el.getXY();
33582             this.adjustAssets();
33583         }
33584     },
33585
33586     // private
33587     adjustAssets : function(doShow){
33588         var x = this.xy[0], y = this.xy[1];
33589         var w = this.size.width, h = this.size.height;
33590         if(doShow === true){
33591             if(this.shadow){
33592                 this.shadow.show(this.el);
33593             }
33594             if(this.shim){
33595                 this.shim.show();
33596             }
33597         }
33598         if(this.shadow && this.shadow.isVisible()){
33599             this.shadow.show(this.el);
33600         }
33601         if(this.shim && this.shim.isVisible()){
33602             this.shim.setBounds(x, y, w, h);
33603         }
33604     },
33605
33606     // private
33607     adjustViewport : function(w, h){
33608         if(!w || !h){
33609             w = Roo.lib.Dom.getViewWidth();
33610             h = Roo.lib.Dom.getViewHeight();
33611         }
33612         // cache the size
33613         this.viewSize = [w, h];
33614         if(this.modal && this.mask.isVisible()){
33615             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33616             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33617         }
33618         if(this.isVisible()){
33619             this.constrainXY();
33620         }
33621     },
33622
33623     /**
33624      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33625      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33626      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33627      */
33628     destroy : function(removeEl){
33629         if(this.isVisible()){
33630             this.animateTarget = null;
33631             this.hide();
33632         }
33633         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33634         if(this.tabs){
33635             this.tabs.destroy(removeEl);
33636         }
33637         Roo.destroy(
33638              this.shim,
33639              this.proxy,
33640              this.resizer,
33641              this.close,
33642              this.mask
33643         );
33644         if(this.dd){
33645             this.dd.unreg();
33646         }
33647         if(this.buttons){
33648            for(var i = 0, len = this.buttons.length; i < len; i++){
33649                this.buttons[i].destroy();
33650            }
33651         }
33652         this.el.removeAllListeners();
33653         if(removeEl === true){
33654             this.el.update("");
33655             this.el.remove();
33656         }
33657         Roo.DialogManager.unregister(this);
33658     },
33659
33660     // private
33661     startMove : function(){
33662         if(this.proxyDrag){
33663             this.proxy.show();
33664         }
33665         if(this.constraintoviewport !== false){
33666             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33667         }
33668     },
33669
33670     // private
33671     endMove : function(){
33672         if(!this.proxyDrag){
33673             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33674         }else{
33675             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33676             this.proxy.hide();
33677         }
33678         this.refreshSize();
33679         this.adjustAssets();
33680         this.focus();
33681         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33682     },
33683
33684     /**
33685      * Brings this dialog to the front of any other visible dialogs
33686      * @return {Roo.BasicDialog} this
33687      */
33688     toFront : function(){
33689         Roo.DialogManager.bringToFront(this);
33690         return this;
33691     },
33692
33693     /**
33694      * Sends this dialog to the back (under) of any other visible dialogs
33695      * @return {Roo.BasicDialog} this
33696      */
33697     toBack : function(){
33698         Roo.DialogManager.sendToBack(this);
33699         return this;
33700     },
33701
33702     /**
33703      * Centers this dialog in the viewport
33704      * @return {Roo.BasicDialog} this
33705      */
33706     center : function(){
33707         var xy = this.el.getCenterXY(true);
33708         this.moveTo(xy[0], xy[1]);
33709         return this;
33710     },
33711
33712     /**
33713      * Moves the dialog's top-left corner to the specified point
33714      * @param {Number} x
33715      * @param {Number} y
33716      * @return {Roo.BasicDialog} this
33717      */
33718     moveTo : function(x, y){
33719         this.xy = [x,y];
33720         if(this.isVisible()){
33721             this.el.setXY(this.xy);
33722             this.adjustAssets();
33723         }
33724         return this;
33725     },
33726
33727     /**
33728      * Aligns the dialog to the specified element
33729      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33730      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33731      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33732      * @return {Roo.BasicDialog} this
33733      */
33734     alignTo : function(element, position, offsets){
33735         this.xy = this.el.getAlignToXY(element, position, offsets);
33736         if(this.isVisible()){
33737             this.el.setXY(this.xy);
33738             this.adjustAssets();
33739         }
33740         return this;
33741     },
33742
33743     /**
33744      * Anchors an element to another element and realigns it when the window is resized.
33745      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33746      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33747      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33748      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33749      * is a number, it is used as the buffer delay (defaults to 50ms).
33750      * @return {Roo.BasicDialog} this
33751      */
33752     anchorTo : function(el, alignment, offsets, monitorScroll){
33753         var action = function(){
33754             this.alignTo(el, alignment, offsets);
33755         };
33756         Roo.EventManager.onWindowResize(action, this);
33757         var tm = typeof monitorScroll;
33758         if(tm != 'undefined'){
33759             Roo.EventManager.on(window, 'scroll', action, this,
33760                 {buffer: tm == 'number' ? monitorScroll : 50});
33761         }
33762         action.call(this);
33763         return this;
33764     },
33765
33766     /**
33767      * Returns true if the dialog is visible
33768      * @return {Boolean}
33769      */
33770     isVisible : function(){
33771         return this.el.isVisible();
33772     },
33773
33774     // private
33775     animHide : function(callback){
33776         var b = Roo.get(this.animateTarget).getBox();
33777         this.proxy.show();
33778         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33779         this.el.hide();
33780         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33781                     this.hideEl.createDelegate(this, [callback]));
33782     },
33783
33784     /**
33785      * Hides the dialog.
33786      * @param {Function} callback (optional) Function to call when the dialog is hidden
33787      * @return {Roo.BasicDialog} this
33788      */
33789     hide : function(callback){
33790         if (this.fireEvent("beforehide", this) === false){
33791             return;
33792         }
33793         if(this.shadow){
33794             this.shadow.hide();
33795         }
33796         if(this.shim) {
33797           this.shim.hide();
33798         }
33799         // sometimes animateTarget seems to get set.. causing problems...
33800         // this just double checks..
33801         if(this.animateTarget && Roo.get(this.animateTarget)) {
33802            this.animHide(callback);
33803         }else{
33804             this.el.hide();
33805             this.hideEl(callback);
33806         }
33807         return this;
33808     },
33809
33810     // private
33811     hideEl : function(callback){
33812         this.proxy.hide();
33813         if(this.modal){
33814             this.mask.hide();
33815             Roo.get(document.body).removeClass("x-body-masked");
33816         }
33817         this.fireEvent("hide", this);
33818         if(typeof callback == "function"){
33819             callback();
33820         }
33821     },
33822
33823     // private
33824     hideAction : function(){
33825         this.setLeft("-10000px");
33826         this.setTop("-10000px");
33827         this.setStyle("visibility", "hidden");
33828     },
33829
33830     // private
33831     refreshSize : function(){
33832         this.size = this.el.getSize();
33833         this.xy = this.el.getXY();
33834         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33835     },
33836
33837     // private
33838     // z-index is managed by the DialogManager and may be overwritten at any time
33839     setZIndex : function(index){
33840         if(this.modal){
33841             this.mask.setStyle("z-index", index);
33842         }
33843         if(this.shim){
33844             this.shim.setStyle("z-index", ++index);
33845         }
33846         if(this.shadow){
33847             this.shadow.setZIndex(++index);
33848         }
33849         this.el.setStyle("z-index", ++index);
33850         if(this.proxy){
33851             this.proxy.setStyle("z-index", ++index);
33852         }
33853         if(this.resizer){
33854             this.resizer.proxy.setStyle("z-index", ++index);
33855         }
33856
33857         this.lastZIndex = index;
33858     },
33859
33860     /**
33861      * Returns the element for this dialog
33862      * @return {Roo.Element} The underlying dialog Element
33863      */
33864     getEl : function(){
33865         return this.el;
33866     }
33867 });
33868
33869 /**
33870  * @class Roo.DialogManager
33871  * Provides global access to BasicDialogs that have been created and
33872  * support for z-indexing (layering) multiple open dialogs.
33873  */
33874 Roo.DialogManager = function(){
33875     var list = {};
33876     var accessList = [];
33877     var front = null;
33878
33879     // private
33880     var sortDialogs = function(d1, d2){
33881         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33882     };
33883
33884     // private
33885     var orderDialogs = function(){
33886         accessList.sort(sortDialogs);
33887         var seed = Roo.DialogManager.zseed;
33888         for(var i = 0, len = accessList.length; i < len; i++){
33889             var dlg = accessList[i];
33890             if(dlg){
33891                 dlg.setZIndex(seed + (i*10));
33892             }
33893         }
33894     };
33895
33896     return {
33897         /**
33898          * The starting z-index for BasicDialogs (defaults to 9000)
33899          * @type Number The z-index value
33900          */
33901         zseed : 9000,
33902
33903         // private
33904         register : function(dlg){
33905             list[dlg.id] = dlg;
33906             accessList.push(dlg);
33907         },
33908
33909         // private
33910         unregister : function(dlg){
33911             delete list[dlg.id];
33912             var i=0;
33913             var len=0;
33914             if(!accessList.indexOf){
33915                 for(  i = 0, len = accessList.length; i < len; i++){
33916                     if(accessList[i] == dlg){
33917                         accessList.splice(i, 1);
33918                         return;
33919                     }
33920                 }
33921             }else{
33922                  i = accessList.indexOf(dlg);
33923                 if(i != -1){
33924                     accessList.splice(i, 1);
33925                 }
33926             }
33927         },
33928
33929         /**
33930          * Gets a registered dialog by id
33931          * @param {String/Object} id The id of the dialog or a dialog
33932          * @return {Roo.BasicDialog} this
33933          */
33934         get : function(id){
33935             return typeof id == "object" ? id : list[id];
33936         },
33937
33938         /**
33939          * Brings the specified dialog to the front
33940          * @param {String/Object} dlg The id of the dialog or a dialog
33941          * @return {Roo.BasicDialog} this
33942          */
33943         bringToFront : function(dlg){
33944             dlg = this.get(dlg);
33945             if(dlg != front){
33946                 front = dlg;
33947                 dlg._lastAccess = new Date().getTime();
33948                 orderDialogs();
33949             }
33950             return dlg;
33951         },
33952
33953         /**
33954          * Sends the specified dialog to the back
33955          * @param {String/Object} dlg The id of the dialog or a dialog
33956          * @return {Roo.BasicDialog} this
33957          */
33958         sendToBack : function(dlg){
33959             dlg = this.get(dlg);
33960             dlg._lastAccess = -(new Date().getTime());
33961             orderDialogs();
33962             return dlg;
33963         },
33964
33965         /**
33966          * Hides all dialogs
33967          */
33968         hideAll : function(){
33969             for(var id in list){
33970                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33971                     list[id].hide();
33972                 }
33973             }
33974         }
33975     };
33976 }();
33977
33978 /**
33979  * @class Roo.LayoutDialog
33980  * @extends Roo.BasicDialog
33981  * @children Roo.ContentPanel
33982  * @parent builder none
33983  * Dialog which provides adjustments for working with a layout in a Dialog.
33984  * Add your necessary layout config options to the dialog's config.<br>
33985  * Example usage (including a nested layout):
33986  * <pre><code>
33987 if(!dialog){
33988     dialog = new Roo.LayoutDialog("download-dlg", {
33989         modal: true,
33990         width:600,
33991         height:450,
33992         shadow:true,
33993         minWidth:500,
33994         minHeight:350,
33995         autoTabs:true,
33996         proxyDrag:true,
33997         // layout config merges with the dialog config
33998         center:{
33999             tabPosition: "top",
34000             alwaysShowTabs: true
34001         }
34002     });
34003     dialog.addKeyListener(27, dialog.hide, dialog);
34004     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34005     dialog.addButton("Build It!", this.getDownload, this);
34006
34007     // we can even add nested layouts
34008     var innerLayout = new Roo.BorderLayout("dl-inner", {
34009         east: {
34010             initialSize: 200,
34011             autoScroll:true,
34012             split:true
34013         },
34014         center: {
34015             autoScroll:true
34016         }
34017     });
34018     innerLayout.beginUpdate();
34019     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34020     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34021     innerLayout.endUpdate(true);
34022
34023     var layout = dialog.getLayout();
34024     layout.beginUpdate();
34025     layout.add("center", new Roo.ContentPanel("standard-panel",
34026                         {title: "Download the Source", fitToFrame:true}));
34027     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34028                {title: "Build your own roo.js"}));
34029     layout.getRegion("center").showPanel(sp);
34030     layout.endUpdate();
34031 }
34032 </code></pre>
34033     * @constructor
34034     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34035     * @param {Object} config configuration options
34036   */
34037 Roo.LayoutDialog = function(el, cfg){
34038     
34039     var config=  cfg;
34040     if (typeof(cfg) == 'undefined') {
34041         config = Roo.apply({}, el);
34042         // not sure why we use documentElement here.. - it should always be body.
34043         // IE7 borks horribly if we use documentElement.
34044         // webkit also does not like documentElement - it creates a body element...
34045         el = Roo.get( document.body || document.documentElement ).createChild();
34046         //config.autoCreate = true;
34047     }
34048     
34049     
34050     config.autoTabs = false;
34051     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34052     this.body.setStyle({overflow:"hidden", position:"relative"});
34053     this.layout = new Roo.BorderLayout(this.body.dom, config);
34054     this.layout.monitorWindowResize = false;
34055     this.el.addClass("x-dlg-auto-layout");
34056     // fix case when center region overwrites center function
34057     this.center = Roo.BasicDialog.prototype.center;
34058     this.on("show", this.layout.layout, this.layout, true);
34059     if (config.items) {
34060         var xitems = config.items;
34061         delete config.items;
34062         Roo.each(xitems, this.addxtype, this);
34063     }
34064     
34065     
34066 };
34067 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34068     
34069     
34070     /**
34071      * @cfg {Roo.LayoutRegion} east  
34072      */
34073     /**
34074      * @cfg {Roo.LayoutRegion} west
34075      */
34076     /**
34077      * @cfg {Roo.LayoutRegion} south
34078      */
34079     /**
34080      * @cfg {Roo.LayoutRegion} north
34081      */
34082     /**
34083      * @cfg {Roo.LayoutRegion} center
34084      */
34085     /**
34086      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34087      */
34088     
34089     
34090     /**
34091      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34092      * @deprecated
34093      */
34094     endUpdate : function(){
34095         this.layout.endUpdate();
34096     },
34097
34098     /**
34099      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34100      *  @deprecated
34101      */
34102     beginUpdate : function(){
34103         this.layout.beginUpdate();
34104     },
34105
34106     /**
34107      * Get the BorderLayout for this dialog
34108      * @return {Roo.BorderLayout}
34109      */
34110     getLayout : function(){
34111         return this.layout;
34112     },
34113
34114     showEl : function(){
34115         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34116         if(Roo.isIE7){
34117             this.layout.layout();
34118         }
34119     },
34120
34121     // private
34122     // Use the syncHeightBeforeShow config option to control this automatically
34123     syncBodyHeight : function(){
34124         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34125         if(this.layout){this.layout.layout();}
34126     },
34127     
34128       /**
34129      * Add an xtype element (actually adds to the layout.)
34130      * @return {Object} xdata xtype object data.
34131      */
34132     
34133     addxtype : function(c) {
34134         return this.layout.addxtype(c);
34135     }
34136 });/*
34137  * Based on:
34138  * Ext JS Library 1.1.1
34139  * Copyright(c) 2006-2007, Ext JS, LLC.
34140  *
34141  * Originally Released Under LGPL - original licence link has changed is not relivant.
34142  *
34143  * Fork - LGPL
34144  * <script type="text/javascript">
34145  */
34146  
34147 /**
34148  * @class Roo.MessageBox
34149  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34150  * Example usage:
34151  *<pre><code>
34152 // Basic alert:
34153 Roo.Msg.alert('Status', 'Changes saved successfully.');
34154
34155 // Prompt for user data:
34156 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34157     if (btn == 'ok'){
34158         // process text value...
34159     }
34160 });
34161
34162 // Show a dialog using config options:
34163 Roo.Msg.show({
34164    title:'Save Changes?',
34165    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34166    buttons: Roo.Msg.YESNOCANCEL,
34167    fn: processResult,
34168    animEl: 'elId'
34169 });
34170 </code></pre>
34171  * @static
34172  */
34173 Roo.MessageBox = function(){
34174     var dlg, opt, mask, waitTimer;
34175     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34176     var buttons, activeTextEl, bwidth;
34177
34178     // private
34179     var handleButton = function(button){
34180         dlg.hide();
34181         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34182     };
34183
34184     // private
34185     var handleHide = function(){
34186         if(opt && opt.cls){
34187             dlg.el.removeClass(opt.cls);
34188         }
34189         if(waitTimer){
34190             Roo.TaskMgr.stop(waitTimer);
34191             waitTimer = null;
34192         }
34193     };
34194
34195     // private
34196     var updateButtons = function(b){
34197         var width = 0;
34198         if(!b){
34199             buttons["ok"].hide();
34200             buttons["cancel"].hide();
34201             buttons["yes"].hide();
34202             buttons["no"].hide();
34203             dlg.footer.dom.style.display = 'none';
34204             return width;
34205         }
34206         dlg.footer.dom.style.display = '';
34207         for(var k in buttons){
34208             if(typeof buttons[k] != "function"){
34209                 if(b[k]){
34210                     buttons[k].show();
34211                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34212                     width += buttons[k].el.getWidth()+15;
34213                 }else{
34214                     buttons[k].hide();
34215                 }
34216             }
34217         }
34218         return width;
34219     };
34220
34221     // private
34222     var handleEsc = function(d, k, e){
34223         if(opt && opt.closable !== false){
34224             dlg.hide();
34225         }
34226         if(e){
34227             e.stopEvent();
34228         }
34229     };
34230
34231     return {
34232         /**
34233          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34234          * @return {Roo.BasicDialog} The BasicDialog element
34235          */
34236         getDialog : function(){
34237            if(!dlg){
34238                 dlg = new Roo.BasicDialog("x-msg-box", {
34239                     autoCreate : true,
34240                     shadow: true,
34241                     draggable: true,
34242                     resizable:false,
34243                     constraintoviewport:false,
34244                     fixedcenter:true,
34245                     collapsible : false,
34246                     shim:true,
34247                     modal: true,
34248                     width:400, height:100,
34249                     buttonAlign:"center",
34250                     closeClick : function(){
34251                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34252                             handleButton("no");
34253                         }else{
34254                             handleButton("cancel");
34255                         }
34256                     }
34257                 });
34258                 dlg.on("hide", handleHide);
34259                 mask = dlg.mask;
34260                 dlg.addKeyListener(27, handleEsc);
34261                 buttons = {};
34262                 var bt = this.buttonText;
34263                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34264                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34265                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34266                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34267                 bodyEl = dlg.body.createChild({
34268
34269                     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>'
34270                 });
34271                 msgEl = bodyEl.dom.firstChild;
34272                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34273                 textboxEl.enableDisplayMode();
34274                 textboxEl.addKeyListener([10,13], function(){
34275                     if(dlg.isVisible() && opt && opt.buttons){
34276                         if(opt.buttons.ok){
34277                             handleButton("ok");
34278                         }else if(opt.buttons.yes){
34279                             handleButton("yes");
34280                         }
34281                     }
34282                 });
34283                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34284                 textareaEl.enableDisplayMode();
34285                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34286                 progressEl.enableDisplayMode();
34287                 var pf = progressEl.dom.firstChild;
34288                 if (pf) {
34289                     pp = Roo.get(pf.firstChild);
34290                     pp.setHeight(pf.offsetHeight);
34291                 }
34292                 
34293             }
34294             return dlg;
34295         },
34296
34297         /**
34298          * Updates the message box body text
34299          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34300          * the XHTML-compliant non-breaking space character '&amp;#160;')
34301          * @return {Roo.MessageBox} This message box
34302          */
34303         updateText : function(text){
34304             if(!dlg.isVisible() && !opt.width){
34305                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34306             }
34307             msgEl.innerHTML = text || '&#160;';
34308       
34309             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34310             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34311             var w = Math.max(
34312                     Math.min(opt.width || cw , this.maxWidth), 
34313                     Math.max(opt.minWidth || this.minWidth, bwidth)
34314             );
34315             if(opt.prompt){
34316                 activeTextEl.setWidth(w);
34317             }
34318             if(dlg.isVisible()){
34319                 dlg.fixedcenter = false;
34320             }
34321             // to big, make it scroll. = But as usual stupid IE does not support
34322             // !important..
34323             
34324             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34325                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34326                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34327             } else {
34328                 bodyEl.dom.style.height = '';
34329                 bodyEl.dom.style.overflowY = '';
34330             }
34331             if (cw > w) {
34332                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34333             } else {
34334                 bodyEl.dom.style.overflowX = '';
34335             }
34336             
34337             dlg.setContentSize(w, bodyEl.getHeight());
34338             if(dlg.isVisible()){
34339                 dlg.fixedcenter = true;
34340             }
34341             return this;
34342         },
34343
34344         /**
34345          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34346          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34347          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34348          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34349          * @return {Roo.MessageBox} This message box
34350          */
34351         updateProgress : function(value, text){
34352             if(text){
34353                 this.updateText(text);
34354             }
34355             if (pp) { // weird bug on my firefox - for some reason this is not defined
34356                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34357             }
34358             return this;
34359         },        
34360
34361         /**
34362          * Returns true if the message box is currently displayed
34363          * @return {Boolean} True if the message box is visible, else false
34364          */
34365         isVisible : function(){
34366             return dlg && dlg.isVisible();  
34367         },
34368
34369         /**
34370          * Hides the message box if it is displayed
34371          */
34372         hide : function(){
34373             if(this.isVisible()){
34374                 dlg.hide();
34375             }  
34376         },
34377
34378         /**
34379          * Displays a new message box, or reinitializes an existing message box, based on the config options
34380          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34381          * The following config object properties are supported:
34382          * <pre>
34383 Property    Type             Description
34384 ----------  ---------------  ------------------------------------------------------------------------------------
34385 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34386                                    closes (defaults to undefined)
34387 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34388                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34389 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34390                                    progress and wait dialogs will ignore this property and always hide the
34391                                    close button as they can only be closed programmatically.
34392 cls               String           A custom CSS class to apply to the message box element
34393 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34394                                    displayed (defaults to 75)
34395 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34396                                    function will be btn (the name of the button that was clicked, if applicable,
34397                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34398                                    Progress and wait dialogs will ignore this option since they do not respond to
34399                                    user actions and can only be closed programmatically, so any required function
34400                                    should be called by the same code after it closes the dialog.
34401 icon              String           A CSS class that provides a background image to be used as an icon for
34402                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34403 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34404 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34405 modal             Boolean          False to allow user interaction with the page while the message box is
34406                                    displayed (defaults to true)
34407 msg               String           A string that will replace the existing message box body text (defaults
34408                                    to the XHTML-compliant non-breaking space character '&#160;')
34409 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34410 progress          Boolean          True to display a progress bar (defaults to false)
34411 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34412 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34413 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34414 title             String           The title text
34415 value             String           The string value to set into the active textbox element if displayed
34416 wait              Boolean          True to display a progress bar (defaults to false)
34417 width             Number           The width of the dialog in pixels
34418 </pre>
34419          *
34420          * Example usage:
34421          * <pre><code>
34422 Roo.Msg.show({
34423    title: 'Address',
34424    msg: 'Please enter your address:',
34425    width: 300,
34426    buttons: Roo.MessageBox.OKCANCEL,
34427    multiline: true,
34428    fn: saveAddress,
34429    animEl: 'addAddressBtn'
34430 });
34431 </code></pre>
34432          * @param {Object} config Configuration options
34433          * @return {Roo.MessageBox} This message box
34434          */
34435         show : function(options)
34436         {
34437             
34438             // this causes nightmares if you show one dialog after another
34439             // especially on callbacks..
34440              
34441             if(this.isVisible()){
34442                 
34443                 this.hide();
34444                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34445                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34446                 Roo.log("New Dialog Message:" +  options.msg )
34447                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34448                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34449                 
34450             }
34451             var d = this.getDialog();
34452             opt = options;
34453             d.setTitle(opt.title || "&#160;");
34454             d.close.setDisplayed(opt.closable !== false);
34455             activeTextEl = textboxEl;
34456             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34457             if(opt.prompt){
34458                 if(opt.multiline){
34459                     textboxEl.hide();
34460                     textareaEl.show();
34461                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34462                         opt.multiline : this.defaultTextHeight);
34463                     activeTextEl = textareaEl;
34464                 }else{
34465                     textboxEl.show();
34466                     textareaEl.hide();
34467                 }
34468             }else{
34469                 textboxEl.hide();
34470                 textareaEl.hide();
34471             }
34472             progressEl.setDisplayed(opt.progress === true);
34473             this.updateProgress(0);
34474             activeTextEl.dom.value = opt.value || "";
34475             if(opt.prompt){
34476                 dlg.setDefaultButton(activeTextEl);
34477             }else{
34478                 var bs = opt.buttons;
34479                 var db = null;
34480                 if(bs && bs.ok){
34481                     db = buttons["ok"];
34482                 }else if(bs && bs.yes){
34483                     db = buttons["yes"];
34484                 }
34485                 dlg.setDefaultButton(db);
34486             }
34487             bwidth = updateButtons(opt.buttons);
34488             this.updateText(opt.msg);
34489             if(opt.cls){
34490                 d.el.addClass(opt.cls);
34491             }
34492             d.proxyDrag = opt.proxyDrag === true;
34493             d.modal = opt.modal !== false;
34494             d.mask = opt.modal !== false ? mask : false;
34495             if(!d.isVisible()){
34496                 // force it to the end of the z-index stack so it gets a cursor in FF
34497                 document.body.appendChild(dlg.el.dom);
34498                 d.animateTarget = null;
34499                 d.show(options.animEl);
34500             }
34501             return this;
34502         },
34503
34504         /**
34505          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34506          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34507          * and closing the message box when the process is complete.
34508          * @param {String} title The title bar text
34509          * @param {String} msg The message box body text
34510          * @return {Roo.MessageBox} This message box
34511          */
34512         progress : function(title, msg){
34513             this.show({
34514                 title : title,
34515                 msg : msg,
34516                 buttons: false,
34517                 progress:true,
34518                 closable:false,
34519                 minWidth: this.minProgressWidth,
34520                 modal : true
34521             });
34522             return this;
34523         },
34524
34525         /**
34526          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34527          * If a callback function is passed it will be called after the user clicks the button, and the
34528          * id of the button that was clicked will be passed as the only parameter to the callback
34529          * (could also be the top-right close button).
34530          * @param {String} title The title bar text
34531          * @param {String} msg The message box body text
34532          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34533          * @param {Object} scope (optional) The scope of the callback function
34534          * @return {Roo.MessageBox} This message box
34535          */
34536         alert : function(title, msg, fn, scope){
34537             this.show({
34538                 title : title,
34539                 msg : msg,
34540                 buttons: this.OK,
34541                 fn: fn,
34542                 scope : scope,
34543                 modal : true
34544             });
34545             return this;
34546         },
34547
34548         /**
34549          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34550          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34551          * You are responsible for closing the message box when the process is complete.
34552          * @param {String} msg The message box body text
34553          * @param {String} title (optional) The title bar text
34554          * @return {Roo.MessageBox} This message box
34555          */
34556         wait : function(msg, title){
34557             this.show({
34558                 title : title,
34559                 msg : msg,
34560                 buttons: false,
34561                 closable:false,
34562                 progress:true,
34563                 modal:true,
34564                 width:300,
34565                 wait:true
34566             });
34567             waitTimer = Roo.TaskMgr.start({
34568                 run: function(i){
34569                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34570                 },
34571                 interval: 1000
34572             });
34573             return this;
34574         },
34575
34576         /**
34577          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34578          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34579          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34580          * @param {String} title The title bar text
34581          * @param {String} msg The message box body text
34582          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34583          * @param {Object} scope (optional) The scope of the callback function
34584          * @return {Roo.MessageBox} This message box
34585          */
34586         confirm : function(title, msg, fn, scope){
34587             this.show({
34588                 title : title,
34589                 msg : msg,
34590                 buttons: this.YESNO,
34591                 fn: fn,
34592                 scope : scope,
34593                 modal : true
34594             });
34595             return this;
34596         },
34597
34598         /**
34599          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34600          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34601          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34602          * (could also be the top-right close button) and the text that was entered will be passed as the two
34603          * parameters to the callback.
34604          * @param {String} title The title bar text
34605          * @param {String} msg The message box body text
34606          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34607          * @param {Object} scope (optional) The scope of the callback function
34608          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34609          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34610          * @return {Roo.MessageBox} This message box
34611          */
34612         prompt : function(title, msg, fn, scope, multiline){
34613             this.show({
34614                 title : title,
34615                 msg : msg,
34616                 buttons: this.OKCANCEL,
34617                 fn: fn,
34618                 minWidth:250,
34619                 scope : scope,
34620                 prompt:true,
34621                 multiline: multiline,
34622                 modal : true
34623             });
34624             return this;
34625         },
34626
34627         /**
34628          * Button config that displays a single OK button
34629          * @type Object
34630          */
34631         OK : {ok:true},
34632         /**
34633          * Button config that displays Yes and No buttons
34634          * @type Object
34635          */
34636         YESNO : {yes:true, no:true},
34637         /**
34638          * Button config that displays OK and Cancel buttons
34639          * @type Object
34640          */
34641         OKCANCEL : {ok:true, cancel:true},
34642         /**
34643          * Button config that displays Yes, No and Cancel buttons
34644          * @type Object
34645          */
34646         YESNOCANCEL : {yes:true, no:true, cancel:true},
34647
34648         /**
34649          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34650          * @type Number
34651          */
34652         defaultTextHeight : 75,
34653         /**
34654          * The maximum width in pixels of the message box (defaults to 600)
34655          * @type Number
34656          */
34657         maxWidth : 600,
34658         /**
34659          * The minimum width in pixels of the message box (defaults to 100)
34660          * @type Number
34661          */
34662         minWidth : 100,
34663         /**
34664          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34665          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34666          * @type Number
34667          */
34668         minProgressWidth : 250,
34669         /**
34670          * An object containing the default button text strings that can be overriden for localized language support.
34671          * Supported properties are: ok, cancel, yes and no.
34672          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34673          * @type Object
34674          */
34675         buttonText : {
34676             ok : "OK",
34677             cancel : "Cancel",
34678             yes : "Yes",
34679             no : "No"
34680         }
34681     };
34682 }();
34683
34684 /**
34685  * Shorthand for {@link Roo.MessageBox}
34686  */
34687 Roo.Msg = Roo.MessageBox;/*
34688  * Based on:
34689  * Ext JS Library 1.1.1
34690  * Copyright(c) 2006-2007, Ext JS, LLC.
34691  *
34692  * Originally Released Under LGPL - original licence link has changed is not relivant.
34693  *
34694  * Fork - LGPL
34695  * <script type="text/javascript">
34696  */
34697 /**
34698  * @class Roo.QuickTips
34699  * Provides attractive and customizable tooltips for any element.
34700  * @static
34701  */
34702 Roo.QuickTips = function(){
34703     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34704     var ce, bd, xy, dd;
34705     var visible = false, disabled = true, inited = false;
34706     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34707     
34708     var onOver = function(e){
34709         if(disabled){
34710             return;
34711         }
34712         var t = e.getTarget();
34713         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34714             return;
34715         }
34716         if(ce && t == ce.el){
34717             clearTimeout(hideProc);
34718             return;
34719         }
34720         if(t && tagEls[t.id]){
34721             tagEls[t.id].el = t;
34722             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34723             return;
34724         }
34725         var ttp, et = Roo.fly(t);
34726         var ns = cfg.namespace;
34727         if(tm.interceptTitles && t.title){
34728             ttp = t.title;
34729             t.qtip = ttp;
34730             t.removeAttribute("title");
34731             e.preventDefault();
34732         }else{
34733             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34734         }
34735         if(ttp){
34736             showProc = show.defer(tm.showDelay, tm, [{
34737                 el: t, 
34738                 text: ttp.replace(/\\n/g,'<br/>'),
34739                 width: et.getAttributeNS(ns, cfg.width),
34740                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34741                 title: et.getAttributeNS(ns, cfg.title),
34742                     cls: et.getAttributeNS(ns, cfg.cls)
34743             }]);
34744         }
34745     };
34746     
34747     var onOut = function(e){
34748         clearTimeout(showProc);
34749         var t = e.getTarget();
34750         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34751             hideProc = setTimeout(hide, tm.hideDelay);
34752         }
34753     };
34754     
34755     var onMove = function(e){
34756         if(disabled){
34757             return;
34758         }
34759         xy = e.getXY();
34760         xy[1] += 18;
34761         if(tm.trackMouse && ce){
34762             el.setXY(xy);
34763         }
34764     };
34765     
34766     var onDown = function(e){
34767         clearTimeout(showProc);
34768         clearTimeout(hideProc);
34769         if(!e.within(el)){
34770             if(tm.hideOnClick){
34771                 hide();
34772                 tm.disable();
34773                 tm.enable.defer(100, tm);
34774             }
34775         }
34776     };
34777     
34778     var getPad = function(){
34779         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34780     };
34781
34782     var show = function(o){
34783         if(disabled){
34784             return;
34785         }
34786         clearTimeout(dismissProc);
34787         ce = o;
34788         if(removeCls){ // in case manually hidden
34789             el.removeClass(removeCls);
34790             removeCls = null;
34791         }
34792         if(ce.cls){
34793             el.addClass(ce.cls);
34794             removeCls = ce.cls;
34795         }
34796         if(ce.title){
34797             tipTitle.update(ce.title);
34798             tipTitle.show();
34799         }else{
34800             tipTitle.update('');
34801             tipTitle.hide();
34802         }
34803         el.dom.style.width  = tm.maxWidth+'px';
34804         //tipBody.dom.style.width = '';
34805         tipBodyText.update(o.text);
34806         var p = getPad(), w = ce.width;
34807         if(!w){
34808             var td = tipBodyText.dom;
34809             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34810             if(aw > tm.maxWidth){
34811                 w = tm.maxWidth;
34812             }else if(aw < tm.minWidth){
34813                 w = tm.minWidth;
34814             }else{
34815                 w = aw;
34816             }
34817         }
34818         //tipBody.setWidth(w);
34819         el.setWidth(parseInt(w, 10) + p);
34820         if(ce.autoHide === false){
34821             close.setDisplayed(true);
34822             if(dd){
34823                 dd.unlock();
34824             }
34825         }else{
34826             close.setDisplayed(false);
34827             if(dd){
34828                 dd.lock();
34829             }
34830         }
34831         if(xy){
34832             el.avoidY = xy[1]-18;
34833             el.setXY(xy);
34834         }
34835         if(tm.animate){
34836             el.setOpacity(.1);
34837             el.setStyle("visibility", "visible");
34838             el.fadeIn({callback: afterShow});
34839         }else{
34840             afterShow();
34841         }
34842     };
34843     
34844     var afterShow = function(){
34845         if(ce){
34846             el.show();
34847             esc.enable();
34848             if(tm.autoDismiss && ce.autoHide !== false){
34849                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34850             }
34851         }
34852     };
34853     
34854     var hide = function(noanim){
34855         clearTimeout(dismissProc);
34856         clearTimeout(hideProc);
34857         ce = null;
34858         if(el.isVisible()){
34859             esc.disable();
34860             if(noanim !== true && tm.animate){
34861                 el.fadeOut({callback: afterHide});
34862             }else{
34863                 afterHide();
34864             } 
34865         }
34866     };
34867     
34868     var afterHide = function(){
34869         el.hide();
34870         if(removeCls){
34871             el.removeClass(removeCls);
34872             removeCls = null;
34873         }
34874     };
34875     
34876     return {
34877         /**
34878         * @cfg {Number} minWidth
34879         * The minimum width of the quick tip (defaults to 40)
34880         */
34881        minWidth : 40,
34882         /**
34883         * @cfg {Number} maxWidth
34884         * The maximum width of the quick tip (defaults to 300)
34885         */
34886        maxWidth : 300,
34887         /**
34888         * @cfg {Boolean} interceptTitles
34889         * True to automatically use the element's DOM title value if available (defaults to false)
34890         */
34891        interceptTitles : false,
34892         /**
34893         * @cfg {Boolean} trackMouse
34894         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34895         */
34896        trackMouse : false,
34897         /**
34898         * @cfg {Boolean} hideOnClick
34899         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34900         */
34901        hideOnClick : true,
34902         /**
34903         * @cfg {Number} showDelay
34904         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34905         */
34906        showDelay : 500,
34907         /**
34908         * @cfg {Number} hideDelay
34909         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34910         */
34911        hideDelay : 200,
34912         /**
34913         * @cfg {Boolean} autoHide
34914         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34915         * Used in conjunction with hideDelay.
34916         */
34917        autoHide : true,
34918         /**
34919         * @cfg {Boolean}
34920         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34921         * (defaults to true).  Used in conjunction with autoDismissDelay.
34922         */
34923        autoDismiss : true,
34924         /**
34925         * @cfg {Number}
34926         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34927         */
34928        autoDismissDelay : 5000,
34929        /**
34930         * @cfg {Boolean} animate
34931         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34932         */
34933        animate : false,
34934
34935        /**
34936         * @cfg {String} title
34937         * Title text to display (defaults to '').  This can be any valid HTML markup.
34938         */
34939         title: '',
34940        /**
34941         * @cfg {String} text
34942         * Body text to display (defaults to '').  This can be any valid HTML markup.
34943         */
34944         text : '',
34945        /**
34946         * @cfg {String} cls
34947         * A CSS class to apply to the base quick tip element (defaults to '').
34948         */
34949         cls : '',
34950        /**
34951         * @cfg {Number} width
34952         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34953         * minWidth or maxWidth.
34954         */
34955         width : null,
34956
34957     /**
34958      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34959      * or display QuickTips in a page.
34960      */
34961        init : function(){
34962           tm = Roo.QuickTips;
34963           cfg = tm.tagConfig;
34964           if(!inited){
34965               if(!Roo.isReady){ // allow calling of init() before onReady
34966                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34967                   return;
34968               }
34969               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34970               el.fxDefaults = {stopFx: true};
34971               // maximum custom styling
34972               //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>');
34973               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>');              
34974               tipTitle = el.child('h3');
34975               tipTitle.enableDisplayMode("block");
34976               tipBody = el.child('div.x-tip-bd');
34977               tipBodyText = el.child('div.x-tip-bd-inner');
34978               //bdLeft = el.child('div.x-tip-bd-left');
34979               //bdRight = el.child('div.x-tip-bd-right');
34980               close = el.child('div.x-tip-close');
34981               close.enableDisplayMode("block");
34982               close.on("click", hide);
34983               var d = Roo.get(document);
34984               d.on("mousedown", onDown);
34985               d.on("mouseover", onOver);
34986               d.on("mouseout", onOut);
34987               d.on("mousemove", onMove);
34988               esc = d.addKeyListener(27, hide);
34989               esc.disable();
34990               if(Roo.dd.DD){
34991                   dd = el.initDD("default", null, {
34992                       onDrag : function(){
34993                           el.sync();  
34994                       }
34995                   });
34996                   dd.setHandleElId(tipTitle.id);
34997                   dd.lock();
34998               }
34999               inited = true;
35000           }
35001           this.enable(); 
35002        },
35003
35004     /**
35005      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35006      * are supported:
35007      * <pre>
35008 Property    Type                   Description
35009 ----------  ---------------------  ------------------------------------------------------------------------
35010 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35011      * </ul>
35012      * @param {Object} config The config object
35013      */
35014        register : function(config){
35015            var cs = config instanceof Array ? config : arguments;
35016            for(var i = 0, len = cs.length; i < len; i++) {
35017                var c = cs[i];
35018                var target = c.target;
35019                if(target){
35020                    if(target instanceof Array){
35021                        for(var j = 0, jlen = target.length; j < jlen; j++){
35022                            tagEls[target[j]] = c;
35023                        }
35024                    }else{
35025                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35026                    }
35027                }
35028            }
35029        },
35030
35031     /**
35032      * Removes this quick tip from its element and destroys it.
35033      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35034      */
35035        unregister : function(el){
35036            delete tagEls[Roo.id(el)];
35037        },
35038
35039     /**
35040      * Enable this quick tip.
35041      */
35042        enable : function(){
35043            if(inited && disabled){
35044                locks.pop();
35045                if(locks.length < 1){
35046                    disabled = false;
35047                }
35048            }
35049        },
35050
35051     /**
35052      * Disable this quick tip.
35053      */
35054        disable : function(){
35055           disabled = true;
35056           clearTimeout(showProc);
35057           clearTimeout(hideProc);
35058           clearTimeout(dismissProc);
35059           if(ce){
35060               hide(true);
35061           }
35062           locks.push(1);
35063        },
35064
35065     /**
35066      * Returns true if the quick tip is enabled, else false.
35067      */
35068        isEnabled : function(){
35069             return !disabled;
35070        },
35071
35072         // private
35073        tagConfig : {
35074            namespace : "roo", // was ext?? this may break..
35075            alt_namespace : "ext",
35076            attribute : "qtip",
35077            width : "width",
35078            target : "target",
35079            title : "qtitle",
35080            hide : "hide",
35081            cls : "qclass"
35082        }
35083    };
35084 }();
35085
35086 // backwards compat
35087 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35088  * Based on:
35089  * Ext JS Library 1.1.1
35090  * Copyright(c) 2006-2007, Ext JS, LLC.
35091  *
35092  * Originally Released Under LGPL - original licence link has changed is not relivant.
35093  *
35094  * Fork - LGPL
35095  * <script type="text/javascript">
35096  */
35097  
35098
35099 /**
35100  * @class Roo.tree.TreePanel
35101  * @extends Roo.data.Tree
35102  * @cfg {Roo.tree.TreeNode} root The root node
35103  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35104  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35105  * @cfg {Boolean} enableDD true to enable drag and drop
35106  * @cfg {Boolean} enableDrag true to enable just drag
35107  * @cfg {Boolean} enableDrop true to enable just drop
35108  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35109  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35110  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35111  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35112  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35113  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35114  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35115  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35116  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35117  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35118  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35119  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35120  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35121  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35122  * @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>
35123  * @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>
35124  * 
35125  * @constructor
35126  * @param {String/HTMLElement/Element} el The container element
35127  * @param {Object} config
35128  */
35129 Roo.tree.TreePanel = function(el, config){
35130     var root = false;
35131     var loader = false;
35132     if (config.root) {
35133         root = config.root;
35134         delete config.root;
35135     }
35136     if (config.loader) {
35137         loader = config.loader;
35138         delete config.loader;
35139     }
35140     
35141     Roo.apply(this, config);
35142     Roo.tree.TreePanel.superclass.constructor.call(this);
35143     this.el = Roo.get(el);
35144     this.el.addClass('x-tree');
35145     //console.log(root);
35146     if (root) {
35147         this.setRootNode( Roo.factory(root, Roo.tree));
35148     }
35149     if (loader) {
35150         this.loader = Roo.factory(loader, Roo.tree);
35151     }
35152    /**
35153     * Read-only. The id of the container element becomes this TreePanel's id.
35154     */
35155     this.id = this.el.id;
35156     this.addEvents({
35157         /**
35158         * @event beforeload
35159         * Fires before a node is loaded, return false to cancel
35160         * @param {Node} node The node being loaded
35161         */
35162         "beforeload" : true,
35163         /**
35164         * @event load
35165         * Fires when a node is loaded
35166         * @param {Node} node The node that was loaded
35167         */
35168         "load" : true,
35169         /**
35170         * @event textchange
35171         * Fires when the text for a node is changed
35172         * @param {Node} node The node
35173         * @param {String} text The new text
35174         * @param {String} oldText The old text
35175         */
35176         "textchange" : true,
35177         /**
35178         * @event beforeexpand
35179         * Fires before a node is expanded, return false to cancel.
35180         * @param {Node} node The node
35181         * @param {Boolean} deep
35182         * @param {Boolean} anim
35183         */
35184         "beforeexpand" : true,
35185         /**
35186         * @event beforecollapse
35187         * Fires before a node is collapsed, return false to cancel.
35188         * @param {Node} node The node
35189         * @param {Boolean} deep
35190         * @param {Boolean} anim
35191         */
35192         "beforecollapse" : true,
35193         /**
35194         * @event expand
35195         * Fires when a node is expanded
35196         * @param {Node} node The node
35197         */
35198         "expand" : true,
35199         /**
35200         * @event disabledchange
35201         * Fires when the disabled status of a node changes
35202         * @param {Node} node The node
35203         * @param {Boolean} disabled
35204         */
35205         "disabledchange" : true,
35206         /**
35207         * @event collapse
35208         * Fires when a node is collapsed
35209         * @param {Node} node The node
35210         */
35211         "collapse" : true,
35212         /**
35213         * @event beforeclick
35214         * Fires before click processing on a node. Return false to cancel the default action.
35215         * @param {Node} node The node
35216         * @param {Roo.EventObject} e The event object
35217         */
35218         "beforeclick":true,
35219         /**
35220         * @event checkchange
35221         * Fires when a node with a checkbox's checked property changes
35222         * @param {Node} this This node
35223         * @param {Boolean} checked
35224         */
35225         "checkchange":true,
35226         /**
35227         * @event click
35228         * Fires when a node is clicked
35229         * @param {Node} node The node
35230         * @param {Roo.EventObject} e The event object
35231         */
35232         "click":true,
35233         /**
35234         * @event dblclick
35235         * Fires when a node is double clicked
35236         * @param {Node} node The node
35237         * @param {Roo.EventObject} e The event object
35238         */
35239         "dblclick":true,
35240         /**
35241         * @event contextmenu
35242         * Fires when a node is right clicked
35243         * @param {Node} node The node
35244         * @param {Roo.EventObject} e The event object
35245         */
35246         "contextmenu":true,
35247         /**
35248         * @event beforechildrenrendered
35249         * Fires right before the child nodes for a node are rendered
35250         * @param {Node} node The node
35251         */
35252         "beforechildrenrendered":true,
35253         /**
35254         * @event startdrag
35255         * Fires when a node starts being dragged
35256         * @param {Roo.tree.TreePanel} this
35257         * @param {Roo.tree.TreeNode} node
35258         * @param {event} e The raw browser event
35259         */ 
35260        "startdrag" : true,
35261        /**
35262         * @event enddrag
35263         * Fires when a drag operation is complete
35264         * @param {Roo.tree.TreePanel} this
35265         * @param {Roo.tree.TreeNode} node
35266         * @param {event} e The raw browser event
35267         */
35268        "enddrag" : true,
35269        /**
35270         * @event dragdrop
35271         * Fires when a dragged node is dropped on a valid DD target
35272         * @param {Roo.tree.TreePanel} this
35273         * @param {Roo.tree.TreeNode} node
35274         * @param {DD} dd The dd it was dropped on
35275         * @param {event} e The raw browser event
35276         */
35277        "dragdrop" : true,
35278        /**
35279         * @event beforenodedrop
35280         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35281         * passed to handlers has the following properties:<br />
35282         * <ul style="padding:5px;padding-left:16px;">
35283         * <li>tree - The TreePanel</li>
35284         * <li>target - The node being targeted for the drop</li>
35285         * <li>data - The drag data from the drag source</li>
35286         * <li>point - The point of the drop - append, above or below</li>
35287         * <li>source - The drag source</li>
35288         * <li>rawEvent - Raw mouse event</li>
35289         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35290         * to be inserted by setting them on this object.</li>
35291         * <li>cancel - Set this to true to cancel the drop.</li>
35292         * </ul>
35293         * @param {Object} dropEvent
35294         */
35295        "beforenodedrop" : true,
35296        /**
35297         * @event nodedrop
35298         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35299         * passed to handlers has the following properties:<br />
35300         * <ul style="padding:5px;padding-left:16px;">
35301         * <li>tree - The TreePanel</li>
35302         * <li>target - The node being targeted for the drop</li>
35303         * <li>data - The drag data from the drag source</li>
35304         * <li>point - The point of the drop - append, above or below</li>
35305         * <li>source - The drag source</li>
35306         * <li>rawEvent - Raw mouse event</li>
35307         * <li>dropNode - Dropped node(s).</li>
35308         * </ul>
35309         * @param {Object} dropEvent
35310         */
35311        "nodedrop" : true,
35312         /**
35313         * @event nodedragover
35314         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35315         * passed to handlers has the following properties:<br />
35316         * <ul style="padding:5px;padding-left:16px;">
35317         * <li>tree - The TreePanel</li>
35318         * <li>target - The node being targeted for the drop</li>
35319         * <li>data - The drag data from the drag source</li>
35320         * <li>point - The point of the drop - append, above or below</li>
35321         * <li>source - The drag source</li>
35322         * <li>rawEvent - Raw mouse event</li>
35323         * <li>dropNode - Drop node(s) provided by the source.</li>
35324         * <li>cancel - Set this to true to signal drop not allowed.</li>
35325         * </ul>
35326         * @param {Object} dragOverEvent
35327         */
35328        "nodedragover" : true,
35329        /**
35330         * @event appendnode
35331         * Fires when append node to the tree
35332         * @param {Roo.tree.TreePanel} this
35333         * @param {Roo.tree.TreeNode} node
35334         * @param {Number} index The index of the newly appended node
35335         */
35336        "appendnode" : true
35337         
35338     });
35339     if(this.singleExpand){
35340        this.on("beforeexpand", this.restrictExpand, this);
35341     }
35342     if (this.editor) {
35343         this.editor.tree = this;
35344         this.editor = Roo.factory(this.editor, Roo.tree);
35345     }
35346     
35347     if (this.selModel) {
35348         this.selModel = Roo.factory(this.selModel, Roo.tree);
35349     }
35350    
35351 };
35352 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35353     rootVisible : true,
35354     animate: Roo.enableFx,
35355     lines : true,
35356     enableDD : false,
35357     hlDrop : Roo.enableFx,
35358   
35359     renderer: false,
35360     
35361     rendererTip: false,
35362     // private
35363     restrictExpand : function(node){
35364         var p = node.parentNode;
35365         if(p){
35366             if(p.expandedChild && p.expandedChild.parentNode == p){
35367                 p.expandedChild.collapse();
35368             }
35369             p.expandedChild = node;
35370         }
35371     },
35372
35373     // private override
35374     setRootNode : function(node){
35375         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35376         if(!this.rootVisible){
35377             node.ui = new Roo.tree.RootTreeNodeUI(node);
35378         }
35379         return node;
35380     },
35381
35382     /**
35383      * Returns the container element for this TreePanel
35384      */
35385     getEl : function(){
35386         return this.el;
35387     },
35388
35389     /**
35390      * Returns the default TreeLoader for this TreePanel
35391      */
35392     getLoader : function(){
35393         return this.loader;
35394     },
35395
35396     /**
35397      * Expand all nodes
35398      */
35399     expandAll : function(){
35400         this.root.expand(true);
35401     },
35402
35403     /**
35404      * Collapse all nodes
35405      */
35406     collapseAll : function(){
35407         this.root.collapse(true);
35408     },
35409
35410     /**
35411      * Returns the selection model used by this TreePanel
35412      */
35413     getSelectionModel : function(){
35414         if(!this.selModel){
35415             this.selModel = new Roo.tree.DefaultSelectionModel();
35416         }
35417         return this.selModel;
35418     },
35419
35420     /**
35421      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35422      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35423      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35424      * @return {Array}
35425      */
35426     getChecked : function(a, startNode){
35427         startNode = startNode || this.root;
35428         var r = [];
35429         var f = function(){
35430             if(this.attributes.checked){
35431                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35432             }
35433         }
35434         startNode.cascade(f);
35435         return r;
35436     },
35437
35438     /**
35439      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35440      * @param {String} path
35441      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35442      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35443      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35444      */
35445     expandPath : function(path, attr, callback){
35446         attr = attr || "id";
35447         var keys = path.split(this.pathSeparator);
35448         var curNode = this.root;
35449         if(curNode.attributes[attr] != keys[1]){ // invalid root
35450             if(callback){
35451                 callback(false, null);
35452             }
35453             return;
35454         }
35455         var index = 1;
35456         var f = function(){
35457             if(++index == keys.length){
35458                 if(callback){
35459                     callback(true, curNode);
35460                 }
35461                 return;
35462             }
35463             var c = curNode.findChild(attr, keys[index]);
35464             if(!c){
35465                 if(callback){
35466                     callback(false, curNode);
35467                 }
35468                 return;
35469             }
35470             curNode = c;
35471             c.expand(false, false, f);
35472         };
35473         curNode.expand(false, false, f);
35474     },
35475
35476     /**
35477      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35478      * @param {String} path
35479      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35480      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35481      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35482      */
35483     selectPath : function(path, attr, callback){
35484         attr = attr || "id";
35485         var keys = path.split(this.pathSeparator);
35486         var v = keys.pop();
35487         if(keys.length > 0){
35488             var f = function(success, node){
35489                 if(success && node){
35490                     var n = node.findChild(attr, v);
35491                     if(n){
35492                         n.select();
35493                         if(callback){
35494                             callback(true, n);
35495                         }
35496                     }else if(callback){
35497                         callback(false, n);
35498                     }
35499                 }else{
35500                     if(callback){
35501                         callback(false, n);
35502                     }
35503                 }
35504             };
35505             this.expandPath(keys.join(this.pathSeparator), attr, f);
35506         }else{
35507             this.root.select();
35508             if(callback){
35509                 callback(true, this.root);
35510             }
35511         }
35512     },
35513
35514     getTreeEl : function(){
35515         return this.el;
35516     },
35517
35518     /**
35519      * Trigger rendering of this TreePanel
35520      */
35521     render : function(){
35522         if (this.innerCt) {
35523             return this; // stop it rendering more than once!!
35524         }
35525         
35526         this.innerCt = this.el.createChild({tag:"ul",
35527                cls:"x-tree-root-ct " +
35528                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35529
35530         if(this.containerScroll){
35531             Roo.dd.ScrollManager.register(this.el);
35532         }
35533         if((this.enableDD || this.enableDrop) && !this.dropZone){
35534            /**
35535             * The dropZone used by this tree if drop is enabled
35536             * @type Roo.tree.TreeDropZone
35537             */
35538              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35539                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35540            });
35541         }
35542         if((this.enableDD || this.enableDrag) && !this.dragZone){
35543            /**
35544             * The dragZone used by this tree if drag is enabled
35545             * @type Roo.tree.TreeDragZone
35546             */
35547             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35548                ddGroup: this.ddGroup || "TreeDD",
35549                scroll: this.ddScroll
35550            });
35551         }
35552         this.getSelectionModel().init(this);
35553         if (!this.root) {
35554             Roo.log("ROOT not set in tree");
35555             return this;
35556         }
35557         this.root.render();
35558         if(!this.rootVisible){
35559             this.root.renderChildren();
35560         }
35561         return this;
35562     }
35563 });/*
35564  * Based on:
35565  * Ext JS Library 1.1.1
35566  * Copyright(c) 2006-2007, Ext JS, LLC.
35567  *
35568  * Originally Released Under LGPL - original licence link has changed is not relivant.
35569  *
35570  * Fork - LGPL
35571  * <script type="text/javascript">
35572  */
35573  
35574
35575 /**
35576  * @class Roo.tree.DefaultSelectionModel
35577  * @extends Roo.util.Observable
35578  * The default single selection for a TreePanel.
35579  * @param {Object} cfg Configuration
35580  */
35581 Roo.tree.DefaultSelectionModel = function(cfg){
35582    this.selNode = null;
35583    
35584    
35585    
35586    this.addEvents({
35587        /**
35588         * @event selectionchange
35589         * Fires when the selected node changes
35590         * @param {DefaultSelectionModel} this
35591         * @param {TreeNode} node the new selection
35592         */
35593        "selectionchange" : true,
35594
35595        /**
35596         * @event beforeselect
35597         * Fires before the selected node changes, return false to cancel the change
35598         * @param {DefaultSelectionModel} this
35599         * @param {TreeNode} node the new selection
35600         * @param {TreeNode} node the old selection
35601         */
35602        "beforeselect" : true
35603    });
35604    
35605     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35606 };
35607
35608 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35609     init : function(tree){
35610         this.tree = tree;
35611         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35612         tree.on("click", this.onNodeClick, this);
35613     },
35614     
35615     onNodeClick : function(node, e){
35616         if (e.ctrlKey && this.selNode == node)  {
35617             this.unselect(node);
35618             return;
35619         }
35620         this.select(node);
35621     },
35622     
35623     /**
35624      * Select a node.
35625      * @param {TreeNode} node The node to select
35626      * @return {TreeNode} The selected node
35627      */
35628     select : function(node){
35629         var last = this.selNode;
35630         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35631             if(last){
35632                 last.ui.onSelectedChange(false);
35633             }
35634             this.selNode = node;
35635             node.ui.onSelectedChange(true);
35636             this.fireEvent("selectionchange", this, node, last);
35637         }
35638         return node;
35639     },
35640     
35641     /**
35642      * Deselect a node.
35643      * @param {TreeNode} node The node to unselect
35644      */
35645     unselect : function(node){
35646         if(this.selNode == node){
35647             this.clearSelections();
35648         }    
35649     },
35650     
35651     /**
35652      * Clear all selections
35653      */
35654     clearSelections : function(){
35655         var n = this.selNode;
35656         if(n){
35657             n.ui.onSelectedChange(false);
35658             this.selNode = null;
35659             this.fireEvent("selectionchange", this, null);
35660         }
35661         return n;
35662     },
35663     
35664     /**
35665      * Get the selected node
35666      * @return {TreeNode} The selected node
35667      */
35668     getSelectedNode : function(){
35669         return this.selNode;    
35670     },
35671     
35672     /**
35673      * Returns true if the node is selected
35674      * @param {TreeNode} node The node to check
35675      * @return {Boolean}
35676      */
35677     isSelected : function(node){
35678         return this.selNode == node;  
35679     },
35680
35681     /**
35682      * Selects the node above the selected node in the tree, intelligently walking the nodes
35683      * @return TreeNode The new selection
35684      */
35685     selectPrevious : function(){
35686         var s = this.selNode || this.lastSelNode;
35687         if(!s){
35688             return null;
35689         }
35690         var ps = s.previousSibling;
35691         if(ps){
35692             if(!ps.isExpanded() || ps.childNodes.length < 1){
35693                 return this.select(ps);
35694             } else{
35695                 var lc = ps.lastChild;
35696                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35697                     lc = lc.lastChild;
35698                 }
35699                 return this.select(lc);
35700             }
35701         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35702             return this.select(s.parentNode);
35703         }
35704         return null;
35705     },
35706
35707     /**
35708      * Selects the node above the selected node in the tree, intelligently walking the nodes
35709      * @return TreeNode The new selection
35710      */
35711     selectNext : function(){
35712         var s = this.selNode || this.lastSelNode;
35713         if(!s){
35714             return null;
35715         }
35716         if(s.firstChild && s.isExpanded()){
35717              return this.select(s.firstChild);
35718          }else if(s.nextSibling){
35719              return this.select(s.nextSibling);
35720          }else if(s.parentNode){
35721             var newS = null;
35722             s.parentNode.bubble(function(){
35723                 if(this.nextSibling){
35724                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35725                     return false;
35726                 }
35727             });
35728             return newS;
35729          }
35730         return null;
35731     },
35732
35733     onKeyDown : function(e){
35734         var s = this.selNode || this.lastSelNode;
35735         // undesirable, but required
35736         var sm = this;
35737         if(!s){
35738             return;
35739         }
35740         var k = e.getKey();
35741         switch(k){
35742              case e.DOWN:
35743                  e.stopEvent();
35744                  this.selectNext();
35745              break;
35746              case e.UP:
35747                  e.stopEvent();
35748                  this.selectPrevious();
35749              break;
35750              case e.RIGHT:
35751                  e.preventDefault();
35752                  if(s.hasChildNodes()){
35753                      if(!s.isExpanded()){
35754                          s.expand();
35755                      }else if(s.firstChild){
35756                          this.select(s.firstChild, e);
35757                      }
35758                  }
35759              break;
35760              case e.LEFT:
35761                  e.preventDefault();
35762                  if(s.hasChildNodes() && s.isExpanded()){
35763                      s.collapse();
35764                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35765                      this.select(s.parentNode, e);
35766                  }
35767              break;
35768         };
35769     }
35770 });
35771
35772 /**
35773  * @class Roo.tree.MultiSelectionModel
35774  * @extends Roo.util.Observable
35775  * Multi selection for a TreePanel.
35776  * @param {Object} cfg Configuration
35777  */
35778 Roo.tree.MultiSelectionModel = function(){
35779    this.selNodes = [];
35780    this.selMap = {};
35781    this.addEvents({
35782        /**
35783         * @event selectionchange
35784         * Fires when the selected nodes change
35785         * @param {MultiSelectionModel} this
35786         * @param {Array} nodes Array of the selected nodes
35787         */
35788        "selectionchange" : true
35789    });
35790    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35791    
35792 };
35793
35794 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35795     init : function(tree){
35796         this.tree = tree;
35797         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35798         tree.on("click", this.onNodeClick, this);
35799     },
35800     
35801     onNodeClick : function(node, e){
35802         this.select(node, e, e.ctrlKey);
35803     },
35804     
35805     /**
35806      * Select a node.
35807      * @param {TreeNode} node The node to select
35808      * @param {EventObject} e (optional) An event associated with the selection
35809      * @param {Boolean} keepExisting True to retain existing selections
35810      * @return {TreeNode} The selected node
35811      */
35812     select : function(node, e, keepExisting){
35813         if(keepExisting !== true){
35814             this.clearSelections(true);
35815         }
35816         if(this.isSelected(node)){
35817             this.lastSelNode = node;
35818             return node;
35819         }
35820         this.selNodes.push(node);
35821         this.selMap[node.id] = node;
35822         this.lastSelNode = node;
35823         node.ui.onSelectedChange(true);
35824         this.fireEvent("selectionchange", this, this.selNodes);
35825         return node;
35826     },
35827     
35828     /**
35829      * Deselect a node.
35830      * @param {TreeNode} node The node to unselect
35831      */
35832     unselect : function(node){
35833         if(this.selMap[node.id]){
35834             node.ui.onSelectedChange(false);
35835             var sn = this.selNodes;
35836             var index = -1;
35837             if(sn.indexOf){
35838                 index = sn.indexOf(node);
35839             }else{
35840                 for(var i = 0, len = sn.length; i < len; i++){
35841                     if(sn[i] == node){
35842                         index = i;
35843                         break;
35844                     }
35845                 }
35846             }
35847             if(index != -1){
35848                 this.selNodes.splice(index, 1);
35849             }
35850             delete this.selMap[node.id];
35851             this.fireEvent("selectionchange", this, this.selNodes);
35852         }
35853     },
35854     
35855     /**
35856      * Clear all selections
35857      */
35858     clearSelections : function(suppressEvent){
35859         var sn = this.selNodes;
35860         if(sn.length > 0){
35861             for(var i = 0, len = sn.length; i < len; i++){
35862                 sn[i].ui.onSelectedChange(false);
35863             }
35864             this.selNodes = [];
35865             this.selMap = {};
35866             if(suppressEvent !== true){
35867                 this.fireEvent("selectionchange", this, this.selNodes);
35868             }
35869         }
35870     },
35871     
35872     /**
35873      * Returns true if the node is selected
35874      * @param {TreeNode} node The node to check
35875      * @return {Boolean}
35876      */
35877     isSelected : function(node){
35878         return this.selMap[node.id] ? true : false;  
35879     },
35880     
35881     /**
35882      * Returns an array of the selected nodes
35883      * @return {Array}
35884      */
35885     getSelectedNodes : function(){
35886         return this.selNodes;    
35887     },
35888
35889     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35890
35891     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35892
35893     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35894 });/*
35895  * Based on:
35896  * Ext JS Library 1.1.1
35897  * Copyright(c) 2006-2007, Ext JS, LLC.
35898  *
35899  * Originally Released Under LGPL - original licence link has changed is not relivant.
35900  *
35901  * Fork - LGPL
35902  * <script type="text/javascript">
35903  */
35904  
35905 /**
35906  * @class Roo.tree.TreeNode
35907  * @extends Roo.data.Node
35908  * @cfg {String} text The text for this node
35909  * @cfg {Boolean} expanded true to start the node expanded
35910  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35911  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35912  * @cfg {Boolean} disabled true to start the node disabled
35913  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35914  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35915  * @cfg {String} cls A css class to be added to the node
35916  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35917  * @cfg {String} href URL of the link used for the node (defaults to #)
35918  * @cfg {String} hrefTarget target frame for the link
35919  * @cfg {String} qtip An Ext QuickTip for the node
35920  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35921  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35922  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35923  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35924  * (defaults to undefined with no checkbox rendered)
35925  * @constructor
35926  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35927  */
35928 Roo.tree.TreeNode = function(attributes){
35929     attributes = attributes || {};
35930     if(typeof attributes == "string"){
35931         attributes = {text: attributes};
35932     }
35933     this.childrenRendered = false;
35934     this.rendered = false;
35935     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35936     this.expanded = attributes.expanded === true;
35937     this.isTarget = attributes.isTarget !== false;
35938     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35939     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35940
35941     /**
35942      * Read-only. The text for this node. To change it use setText().
35943      * @type String
35944      */
35945     this.text = attributes.text;
35946     /**
35947      * True if this node is disabled.
35948      * @type Boolean
35949      */
35950     this.disabled = attributes.disabled === true;
35951
35952     this.addEvents({
35953         /**
35954         * @event textchange
35955         * Fires when the text for this node is changed
35956         * @param {Node} this This node
35957         * @param {String} text The new text
35958         * @param {String} oldText The old text
35959         */
35960         "textchange" : true,
35961         /**
35962         * @event beforeexpand
35963         * Fires before this node is expanded, return false to cancel.
35964         * @param {Node} this This node
35965         * @param {Boolean} deep
35966         * @param {Boolean} anim
35967         */
35968         "beforeexpand" : true,
35969         /**
35970         * @event beforecollapse
35971         * Fires before this node is collapsed, return false to cancel.
35972         * @param {Node} this This node
35973         * @param {Boolean} deep
35974         * @param {Boolean} anim
35975         */
35976         "beforecollapse" : true,
35977         /**
35978         * @event expand
35979         * Fires when this node is expanded
35980         * @param {Node} this This node
35981         */
35982         "expand" : true,
35983         /**
35984         * @event disabledchange
35985         * Fires when the disabled status of this node changes
35986         * @param {Node} this This node
35987         * @param {Boolean} disabled
35988         */
35989         "disabledchange" : true,
35990         /**
35991         * @event collapse
35992         * Fires when this node is collapsed
35993         * @param {Node} this This node
35994         */
35995         "collapse" : true,
35996         /**
35997         * @event beforeclick
35998         * Fires before click processing. Return false to cancel the default action.
35999         * @param {Node} this This node
36000         * @param {Roo.EventObject} e The event object
36001         */
36002         "beforeclick":true,
36003         /**
36004         * @event checkchange
36005         * Fires when a node with a checkbox's checked property changes
36006         * @param {Node} this This node
36007         * @param {Boolean} checked
36008         */
36009         "checkchange":true,
36010         /**
36011         * @event click
36012         * Fires when this node is clicked
36013         * @param {Node} this This node
36014         * @param {Roo.EventObject} e The event object
36015         */
36016         "click":true,
36017         /**
36018         * @event dblclick
36019         * Fires when this node is double clicked
36020         * @param {Node} this This node
36021         * @param {Roo.EventObject} e The event object
36022         */
36023         "dblclick":true,
36024         /**
36025         * @event contextmenu
36026         * Fires when this node is right clicked
36027         * @param {Node} this This node
36028         * @param {Roo.EventObject} e The event object
36029         */
36030         "contextmenu":true,
36031         /**
36032         * @event beforechildrenrendered
36033         * Fires right before the child nodes for this node are rendered
36034         * @param {Node} this This node
36035         */
36036         "beforechildrenrendered":true
36037     });
36038
36039     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36040
36041     /**
36042      * Read-only. The UI for this node
36043      * @type TreeNodeUI
36044      */
36045     this.ui = new uiClass(this);
36046     
36047     // finally support items[]
36048     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36049         return;
36050     }
36051     
36052     
36053     Roo.each(this.attributes.items, function(c) {
36054         this.appendChild(Roo.factory(c,Roo.Tree));
36055     }, this);
36056     delete this.attributes.items;
36057     
36058     
36059     
36060 };
36061 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36062     preventHScroll: true,
36063     /**
36064      * Returns true if this node is expanded
36065      * @return {Boolean}
36066      */
36067     isExpanded : function(){
36068         return this.expanded;
36069     },
36070
36071     /**
36072      * Returns the UI object for this node
36073      * @return {TreeNodeUI}
36074      */
36075     getUI : function(){
36076         return this.ui;
36077     },
36078
36079     // private override
36080     setFirstChild : function(node){
36081         var of = this.firstChild;
36082         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36083         if(this.childrenRendered && of && node != of){
36084             of.renderIndent(true, true);
36085         }
36086         if(this.rendered){
36087             this.renderIndent(true, true);
36088         }
36089     },
36090
36091     // private override
36092     setLastChild : function(node){
36093         var ol = this.lastChild;
36094         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36095         if(this.childrenRendered && ol && node != ol){
36096             ol.renderIndent(true, true);
36097         }
36098         if(this.rendered){
36099             this.renderIndent(true, true);
36100         }
36101     },
36102
36103     // these methods are overridden to provide lazy rendering support
36104     // private override
36105     appendChild : function()
36106     {
36107         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36108         if(node && this.childrenRendered){
36109             node.render();
36110         }
36111         this.ui.updateExpandIcon();
36112         return node;
36113     },
36114
36115     // private override
36116     removeChild : function(node){
36117         this.ownerTree.getSelectionModel().unselect(node);
36118         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36119         // if it's been rendered remove dom node
36120         if(this.childrenRendered){
36121             node.ui.remove();
36122         }
36123         if(this.childNodes.length < 1){
36124             this.collapse(false, false);
36125         }else{
36126             this.ui.updateExpandIcon();
36127         }
36128         if(!this.firstChild) {
36129             this.childrenRendered = false;
36130         }
36131         return node;
36132     },
36133
36134     // private override
36135     insertBefore : function(node, refNode){
36136         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36137         if(newNode && refNode && this.childrenRendered){
36138             node.render();
36139         }
36140         this.ui.updateExpandIcon();
36141         return newNode;
36142     },
36143
36144     /**
36145      * Sets the text for this node
36146      * @param {String} text
36147      */
36148     setText : function(text){
36149         var oldText = this.text;
36150         this.text = text;
36151         this.attributes.text = text;
36152         if(this.rendered){ // event without subscribing
36153             this.ui.onTextChange(this, text, oldText);
36154         }
36155         this.fireEvent("textchange", this, text, oldText);
36156     },
36157
36158     /**
36159      * Triggers selection of this node
36160      */
36161     select : function(){
36162         this.getOwnerTree().getSelectionModel().select(this);
36163     },
36164
36165     /**
36166      * Triggers deselection of this node
36167      */
36168     unselect : function(){
36169         this.getOwnerTree().getSelectionModel().unselect(this);
36170     },
36171
36172     /**
36173      * Returns true if this node is selected
36174      * @return {Boolean}
36175      */
36176     isSelected : function(){
36177         return this.getOwnerTree().getSelectionModel().isSelected(this);
36178     },
36179
36180     /**
36181      * Expand this node.
36182      * @param {Boolean} deep (optional) True to expand all children as well
36183      * @param {Boolean} anim (optional) false to cancel the default animation
36184      * @param {Function} callback (optional) A callback to be called when
36185      * expanding this node completes (does not wait for deep expand to complete).
36186      * Called with 1 parameter, this node.
36187      */
36188     expand : function(deep, anim, callback){
36189         if(!this.expanded){
36190             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36191                 return;
36192             }
36193             if(!this.childrenRendered){
36194                 this.renderChildren();
36195             }
36196             this.expanded = true;
36197             
36198             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36199                 this.ui.animExpand(function(){
36200                     this.fireEvent("expand", this);
36201                     if(typeof callback == "function"){
36202                         callback(this);
36203                     }
36204                     if(deep === true){
36205                         this.expandChildNodes(true);
36206                     }
36207                 }.createDelegate(this));
36208                 return;
36209             }else{
36210                 this.ui.expand();
36211                 this.fireEvent("expand", this);
36212                 if(typeof callback == "function"){
36213                     callback(this);
36214                 }
36215             }
36216         }else{
36217            if(typeof callback == "function"){
36218                callback(this);
36219            }
36220         }
36221         if(deep === true){
36222             this.expandChildNodes(true);
36223         }
36224     },
36225
36226     isHiddenRoot : function(){
36227         return this.isRoot && !this.getOwnerTree().rootVisible;
36228     },
36229
36230     /**
36231      * Collapse this node.
36232      * @param {Boolean} deep (optional) True to collapse all children as well
36233      * @param {Boolean} anim (optional) false to cancel the default animation
36234      */
36235     collapse : function(deep, anim){
36236         if(this.expanded && !this.isHiddenRoot()){
36237             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36238                 return;
36239             }
36240             this.expanded = false;
36241             if((this.getOwnerTree().animate && anim !== false) || anim){
36242                 this.ui.animCollapse(function(){
36243                     this.fireEvent("collapse", this);
36244                     if(deep === true){
36245                         this.collapseChildNodes(true);
36246                     }
36247                 }.createDelegate(this));
36248                 return;
36249             }else{
36250                 this.ui.collapse();
36251                 this.fireEvent("collapse", this);
36252             }
36253         }
36254         if(deep === true){
36255             var cs = this.childNodes;
36256             for(var i = 0, len = cs.length; i < len; i++) {
36257                 cs[i].collapse(true, false);
36258             }
36259         }
36260     },
36261
36262     // private
36263     delayedExpand : function(delay){
36264         if(!this.expandProcId){
36265             this.expandProcId = this.expand.defer(delay, this);
36266         }
36267     },
36268
36269     // private
36270     cancelExpand : function(){
36271         if(this.expandProcId){
36272             clearTimeout(this.expandProcId);
36273         }
36274         this.expandProcId = false;
36275     },
36276
36277     /**
36278      * Toggles expanded/collapsed state of the node
36279      */
36280     toggle : function(){
36281         if(this.expanded){
36282             this.collapse();
36283         }else{
36284             this.expand();
36285         }
36286     },
36287
36288     /**
36289      * Ensures all parent nodes are expanded
36290      */
36291     ensureVisible : function(callback){
36292         var tree = this.getOwnerTree();
36293         tree.expandPath(this.parentNode.getPath(), false, function(){
36294             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36295             Roo.callback(callback);
36296         }.createDelegate(this));
36297     },
36298
36299     /**
36300      * Expand all child nodes
36301      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36302      */
36303     expandChildNodes : function(deep){
36304         var cs = this.childNodes;
36305         for(var i = 0, len = cs.length; i < len; i++) {
36306                 cs[i].expand(deep);
36307         }
36308     },
36309
36310     /**
36311      * Collapse all child nodes
36312      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36313      */
36314     collapseChildNodes : function(deep){
36315         var cs = this.childNodes;
36316         for(var i = 0, len = cs.length; i < len; i++) {
36317                 cs[i].collapse(deep);
36318         }
36319     },
36320
36321     /**
36322      * Disables this node
36323      */
36324     disable : function(){
36325         this.disabled = true;
36326         this.unselect();
36327         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36328             this.ui.onDisableChange(this, true);
36329         }
36330         this.fireEvent("disabledchange", this, true);
36331     },
36332
36333     /**
36334      * Enables this node
36335      */
36336     enable : function(){
36337         this.disabled = false;
36338         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36339             this.ui.onDisableChange(this, false);
36340         }
36341         this.fireEvent("disabledchange", this, false);
36342     },
36343
36344     // private
36345     renderChildren : function(suppressEvent){
36346         if(suppressEvent !== false){
36347             this.fireEvent("beforechildrenrendered", this);
36348         }
36349         var cs = this.childNodes;
36350         for(var i = 0, len = cs.length; i < len; i++){
36351             cs[i].render(true);
36352         }
36353         this.childrenRendered = true;
36354     },
36355
36356     // private
36357     sort : function(fn, scope){
36358         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36359         if(this.childrenRendered){
36360             var cs = this.childNodes;
36361             for(var i = 0, len = cs.length; i < len; i++){
36362                 cs[i].render(true);
36363             }
36364         }
36365     },
36366
36367     // private
36368     render : function(bulkRender){
36369         this.ui.render(bulkRender);
36370         if(!this.rendered){
36371             this.rendered = true;
36372             if(this.expanded){
36373                 this.expanded = false;
36374                 this.expand(false, false);
36375             }
36376         }
36377     },
36378
36379     // private
36380     renderIndent : function(deep, refresh){
36381         if(refresh){
36382             this.ui.childIndent = null;
36383         }
36384         this.ui.renderIndent();
36385         if(deep === true && this.childrenRendered){
36386             var cs = this.childNodes;
36387             for(var i = 0, len = cs.length; i < len; i++){
36388                 cs[i].renderIndent(true, refresh);
36389             }
36390         }
36391     }
36392 });/*
36393  * Based on:
36394  * Ext JS Library 1.1.1
36395  * Copyright(c) 2006-2007, Ext JS, LLC.
36396  *
36397  * Originally Released Under LGPL - original licence link has changed is not relivant.
36398  *
36399  * Fork - LGPL
36400  * <script type="text/javascript">
36401  */
36402  
36403 /**
36404  * @class Roo.tree.AsyncTreeNode
36405  * @extends Roo.tree.TreeNode
36406  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36407  * @constructor
36408  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36409  */
36410  Roo.tree.AsyncTreeNode = function(config){
36411     this.loaded = false;
36412     this.loading = false;
36413     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36414     /**
36415     * @event beforeload
36416     * Fires before this node is loaded, return false to cancel
36417     * @param {Node} this This node
36418     */
36419     this.addEvents({'beforeload':true, 'load': true});
36420     /**
36421     * @event load
36422     * Fires when this node is loaded
36423     * @param {Node} this This node
36424     */
36425     /**
36426      * The loader used by this node (defaults to using the tree's defined loader)
36427      * @type TreeLoader
36428      * @property loader
36429      */
36430 };
36431 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36432     expand : function(deep, anim, callback){
36433         if(this.loading){ // if an async load is already running, waiting til it's done
36434             var timer;
36435             var f = function(){
36436                 if(!this.loading){ // done loading
36437                     clearInterval(timer);
36438                     this.expand(deep, anim, callback);
36439                 }
36440             }.createDelegate(this);
36441             timer = setInterval(f, 200);
36442             return;
36443         }
36444         if(!this.loaded){
36445             if(this.fireEvent("beforeload", this) === false){
36446                 return;
36447             }
36448             this.loading = true;
36449             this.ui.beforeLoad(this);
36450             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36451             if(loader){
36452                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36453                 return;
36454             }
36455         }
36456         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36457     },
36458     
36459     /**
36460      * Returns true if this node is currently loading
36461      * @return {Boolean}
36462      */
36463     isLoading : function(){
36464         return this.loading;  
36465     },
36466     
36467     loadComplete : function(deep, anim, callback){
36468         this.loading = false;
36469         this.loaded = true;
36470         this.ui.afterLoad(this);
36471         this.fireEvent("load", this);
36472         this.expand(deep, anim, callback);
36473     },
36474     
36475     /**
36476      * Returns true if this node has been loaded
36477      * @return {Boolean}
36478      */
36479     isLoaded : function(){
36480         return this.loaded;
36481     },
36482     
36483     hasChildNodes : function(){
36484         if(!this.isLeaf() && !this.loaded){
36485             return true;
36486         }else{
36487             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36488         }
36489     },
36490
36491     /**
36492      * Trigger a reload for this node
36493      * @param {Function} callback
36494      */
36495     reload : function(callback){
36496         this.collapse(false, false);
36497         while(this.firstChild){
36498             this.removeChild(this.firstChild);
36499         }
36500         this.childrenRendered = false;
36501         this.loaded = false;
36502         if(this.isHiddenRoot()){
36503             this.expanded = false;
36504         }
36505         this.expand(false, false, callback);
36506     }
36507 });/*
36508  * Based on:
36509  * Ext JS Library 1.1.1
36510  * Copyright(c) 2006-2007, Ext JS, LLC.
36511  *
36512  * Originally Released Under LGPL - original licence link has changed is not relivant.
36513  *
36514  * Fork - LGPL
36515  * <script type="text/javascript">
36516  */
36517  
36518 /**
36519  * @class Roo.tree.TreeNodeUI
36520  * @constructor
36521  * @param {Object} node The node to render
36522  * The TreeNode UI implementation is separate from the
36523  * tree implementation. Unless you are customizing the tree UI,
36524  * you should never have to use this directly.
36525  */
36526 Roo.tree.TreeNodeUI = function(node){
36527     this.node = node;
36528     this.rendered = false;
36529     this.animating = false;
36530     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36531 };
36532
36533 Roo.tree.TreeNodeUI.prototype = {
36534     removeChild : function(node){
36535         if(this.rendered){
36536             this.ctNode.removeChild(node.ui.getEl());
36537         }
36538     },
36539
36540     beforeLoad : function(){
36541          this.addClass("x-tree-node-loading");
36542     },
36543
36544     afterLoad : function(){
36545          this.removeClass("x-tree-node-loading");
36546     },
36547
36548     onTextChange : function(node, text, oldText){
36549         if(this.rendered){
36550             this.textNode.innerHTML = text;
36551         }
36552     },
36553
36554     onDisableChange : function(node, state){
36555         this.disabled = state;
36556         if(state){
36557             this.addClass("x-tree-node-disabled");
36558         }else{
36559             this.removeClass("x-tree-node-disabled");
36560         }
36561     },
36562
36563     onSelectedChange : function(state){
36564         if(state){
36565             this.focus();
36566             this.addClass("x-tree-selected");
36567         }else{
36568             //this.blur();
36569             this.removeClass("x-tree-selected");
36570         }
36571     },
36572
36573     onMove : function(tree, node, oldParent, newParent, index, refNode){
36574         this.childIndent = null;
36575         if(this.rendered){
36576             var targetNode = newParent.ui.getContainer();
36577             if(!targetNode){//target not rendered
36578                 this.holder = document.createElement("div");
36579                 this.holder.appendChild(this.wrap);
36580                 return;
36581             }
36582             var insertBefore = refNode ? refNode.ui.getEl() : null;
36583             if(insertBefore){
36584                 targetNode.insertBefore(this.wrap, insertBefore);
36585             }else{
36586                 targetNode.appendChild(this.wrap);
36587             }
36588             this.node.renderIndent(true);
36589         }
36590     },
36591
36592     addClass : function(cls){
36593         if(this.elNode){
36594             Roo.fly(this.elNode).addClass(cls);
36595         }
36596     },
36597
36598     removeClass : function(cls){
36599         if(this.elNode){
36600             Roo.fly(this.elNode).removeClass(cls);
36601         }
36602     },
36603
36604     remove : function(){
36605         if(this.rendered){
36606             this.holder = document.createElement("div");
36607             this.holder.appendChild(this.wrap);
36608         }
36609     },
36610
36611     fireEvent : function(){
36612         return this.node.fireEvent.apply(this.node, arguments);
36613     },
36614
36615     initEvents : function(){
36616         this.node.on("move", this.onMove, this);
36617         var E = Roo.EventManager;
36618         var a = this.anchor;
36619
36620         var el = Roo.fly(a, '_treeui');
36621
36622         if(Roo.isOpera){ // opera render bug ignores the CSS
36623             el.setStyle("text-decoration", "none");
36624         }
36625
36626         el.on("click", this.onClick, this);
36627         el.on("dblclick", this.onDblClick, this);
36628
36629         if(this.checkbox){
36630             Roo.EventManager.on(this.checkbox,
36631                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36632         }
36633
36634         el.on("contextmenu", this.onContextMenu, this);
36635
36636         var icon = Roo.fly(this.iconNode);
36637         icon.on("click", this.onClick, this);
36638         icon.on("dblclick", this.onDblClick, this);
36639         icon.on("contextmenu", this.onContextMenu, this);
36640         E.on(this.ecNode, "click", this.ecClick, this, true);
36641
36642         if(this.node.disabled){
36643             this.addClass("x-tree-node-disabled");
36644         }
36645         if(this.node.hidden){
36646             this.addClass("x-tree-node-disabled");
36647         }
36648         var ot = this.node.getOwnerTree();
36649         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36650         if(dd && (!this.node.isRoot || ot.rootVisible)){
36651             Roo.dd.Registry.register(this.elNode, {
36652                 node: this.node,
36653                 handles: this.getDDHandles(),
36654                 isHandle: false
36655             });
36656         }
36657     },
36658
36659     getDDHandles : function(){
36660         return [this.iconNode, this.textNode];
36661     },
36662
36663     hide : function(){
36664         if(this.rendered){
36665             this.wrap.style.display = "none";
36666         }
36667     },
36668
36669     show : function(){
36670         if(this.rendered){
36671             this.wrap.style.display = "";
36672         }
36673     },
36674
36675     onContextMenu : function(e){
36676         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36677             e.preventDefault();
36678             this.focus();
36679             this.fireEvent("contextmenu", this.node, e);
36680         }
36681     },
36682
36683     onClick : function(e){
36684         if(this.dropping){
36685             e.stopEvent();
36686             return;
36687         }
36688         if(this.fireEvent("beforeclick", this.node, e) !== false){
36689             if(!this.disabled && this.node.attributes.href){
36690                 this.fireEvent("click", this.node, e);
36691                 return;
36692             }
36693             e.preventDefault();
36694             if(this.disabled){
36695                 return;
36696             }
36697
36698             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36699                 this.node.toggle();
36700             }
36701
36702             this.fireEvent("click", this.node, e);
36703         }else{
36704             e.stopEvent();
36705         }
36706     },
36707
36708     onDblClick : function(e){
36709         e.preventDefault();
36710         if(this.disabled){
36711             return;
36712         }
36713         if(this.checkbox){
36714             this.toggleCheck();
36715         }
36716         if(!this.animating && this.node.hasChildNodes()){
36717             this.node.toggle();
36718         }
36719         this.fireEvent("dblclick", this.node, e);
36720     },
36721
36722     onCheckChange : function(){
36723         var checked = this.checkbox.checked;
36724         this.node.attributes.checked = checked;
36725         this.fireEvent('checkchange', this.node, checked);
36726     },
36727
36728     ecClick : function(e){
36729         if(!this.animating && this.node.hasChildNodes()){
36730             this.node.toggle();
36731         }
36732     },
36733
36734     startDrop : function(){
36735         this.dropping = true;
36736     },
36737
36738     // delayed drop so the click event doesn't get fired on a drop
36739     endDrop : function(){
36740        setTimeout(function(){
36741            this.dropping = false;
36742        }.createDelegate(this), 50);
36743     },
36744
36745     expand : function(){
36746         this.updateExpandIcon();
36747         this.ctNode.style.display = "";
36748     },
36749
36750     focus : function(){
36751         if(!this.node.preventHScroll){
36752             try{this.anchor.focus();
36753             }catch(e){}
36754         }else if(!Roo.isIE){
36755             try{
36756                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36757                 var l = noscroll.scrollLeft;
36758                 this.anchor.focus();
36759                 noscroll.scrollLeft = l;
36760             }catch(e){}
36761         }
36762     },
36763
36764     toggleCheck : function(value){
36765         var cb = this.checkbox;
36766         if(cb){
36767             cb.checked = (value === undefined ? !cb.checked : value);
36768         }
36769     },
36770
36771     blur : function(){
36772         try{
36773             this.anchor.blur();
36774         }catch(e){}
36775     },
36776
36777     animExpand : function(callback){
36778         var ct = Roo.get(this.ctNode);
36779         ct.stopFx();
36780         if(!this.node.hasChildNodes()){
36781             this.updateExpandIcon();
36782             this.ctNode.style.display = "";
36783             Roo.callback(callback);
36784             return;
36785         }
36786         this.animating = true;
36787         this.updateExpandIcon();
36788
36789         ct.slideIn('t', {
36790            callback : function(){
36791                this.animating = false;
36792                Roo.callback(callback);
36793             },
36794             scope: this,
36795             duration: this.node.ownerTree.duration || .25
36796         });
36797     },
36798
36799     highlight : function(){
36800         var tree = this.node.getOwnerTree();
36801         Roo.fly(this.wrap).highlight(
36802             tree.hlColor || "C3DAF9",
36803             {endColor: tree.hlBaseColor}
36804         );
36805     },
36806
36807     collapse : function(){
36808         this.updateExpandIcon();
36809         this.ctNode.style.display = "none";
36810     },
36811
36812     animCollapse : function(callback){
36813         var ct = Roo.get(this.ctNode);
36814         ct.enableDisplayMode('block');
36815         ct.stopFx();
36816
36817         this.animating = true;
36818         this.updateExpandIcon();
36819
36820         ct.slideOut('t', {
36821             callback : function(){
36822                this.animating = false;
36823                Roo.callback(callback);
36824             },
36825             scope: this,
36826             duration: this.node.ownerTree.duration || .25
36827         });
36828     },
36829
36830     getContainer : function(){
36831         return this.ctNode;
36832     },
36833
36834     getEl : function(){
36835         return this.wrap;
36836     },
36837
36838     appendDDGhost : function(ghostNode){
36839         ghostNode.appendChild(this.elNode.cloneNode(true));
36840     },
36841
36842     getDDRepairXY : function(){
36843         return Roo.lib.Dom.getXY(this.iconNode);
36844     },
36845
36846     onRender : function(){
36847         this.render();
36848     },
36849
36850     render : function(bulkRender){
36851         var n = this.node, a = n.attributes;
36852         var targetNode = n.parentNode ?
36853               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36854
36855         if(!this.rendered){
36856             this.rendered = true;
36857
36858             this.renderElements(n, a, targetNode, bulkRender);
36859
36860             if(a.qtip){
36861                if(this.textNode.setAttributeNS){
36862                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36863                    if(a.qtipTitle){
36864                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36865                    }
36866                }else{
36867                    this.textNode.setAttribute("ext:qtip", a.qtip);
36868                    if(a.qtipTitle){
36869                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36870                    }
36871                }
36872             }else if(a.qtipCfg){
36873                 a.qtipCfg.target = Roo.id(this.textNode);
36874                 Roo.QuickTips.register(a.qtipCfg);
36875             }
36876             this.initEvents();
36877             if(!this.node.expanded){
36878                 this.updateExpandIcon();
36879             }
36880         }else{
36881             if(bulkRender === true) {
36882                 targetNode.appendChild(this.wrap);
36883             }
36884         }
36885     },
36886
36887     renderElements : function(n, a, targetNode, bulkRender)
36888     {
36889         // add some indent caching, this helps performance when rendering a large tree
36890         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36891         var t = n.getOwnerTree();
36892         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36893         if (typeof(n.attributes.html) != 'undefined') {
36894             txt = n.attributes.html;
36895         }
36896         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36897         var cb = typeof a.checked == 'boolean';
36898         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36899         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36900             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36901             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36902             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36903             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36904             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36905              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36906                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36907             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36908             "</li>"];
36909
36910         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36911             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36912                                 n.nextSibling.ui.getEl(), buf.join(""));
36913         }else{
36914             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36915         }
36916
36917         this.elNode = this.wrap.childNodes[0];
36918         this.ctNode = this.wrap.childNodes[1];
36919         var cs = this.elNode.childNodes;
36920         this.indentNode = cs[0];
36921         this.ecNode = cs[1];
36922         this.iconNode = cs[2];
36923         var index = 3;
36924         if(cb){
36925             this.checkbox = cs[3];
36926             index++;
36927         }
36928         this.anchor = cs[index];
36929         this.textNode = cs[index].firstChild;
36930     },
36931
36932     getAnchor : function(){
36933         return this.anchor;
36934     },
36935
36936     getTextEl : function(){
36937         return this.textNode;
36938     },
36939
36940     getIconEl : function(){
36941         return this.iconNode;
36942     },
36943
36944     isChecked : function(){
36945         return this.checkbox ? this.checkbox.checked : false;
36946     },
36947
36948     updateExpandIcon : function(){
36949         if(this.rendered){
36950             var n = this.node, c1, c2;
36951             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36952             var hasChild = n.hasChildNodes();
36953             if(hasChild){
36954                 if(n.expanded){
36955                     cls += "-minus";
36956                     c1 = "x-tree-node-collapsed";
36957                     c2 = "x-tree-node-expanded";
36958                 }else{
36959                     cls += "-plus";
36960                     c1 = "x-tree-node-expanded";
36961                     c2 = "x-tree-node-collapsed";
36962                 }
36963                 if(this.wasLeaf){
36964                     this.removeClass("x-tree-node-leaf");
36965                     this.wasLeaf = false;
36966                 }
36967                 if(this.c1 != c1 || this.c2 != c2){
36968                     Roo.fly(this.elNode).replaceClass(c1, c2);
36969                     this.c1 = c1; this.c2 = c2;
36970                 }
36971             }else{
36972                 // this changes non-leafs into leafs if they have no children.
36973                 // it's not very rational behaviour..
36974                 
36975                 if(!this.wasLeaf && this.node.leaf){
36976                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36977                     delete this.c1;
36978                     delete this.c2;
36979                     this.wasLeaf = true;
36980                 }
36981             }
36982             var ecc = "x-tree-ec-icon "+cls;
36983             if(this.ecc != ecc){
36984                 this.ecNode.className = ecc;
36985                 this.ecc = ecc;
36986             }
36987         }
36988     },
36989
36990     getChildIndent : function(){
36991         if(!this.childIndent){
36992             var buf = [];
36993             var p = this.node;
36994             while(p){
36995                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36996                     if(!p.isLast()) {
36997                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36998                     } else {
36999                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37000                     }
37001                 }
37002                 p = p.parentNode;
37003             }
37004             this.childIndent = buf.join("");
37005         }
37006         return this.childIndent;
37007     },
37008
37009     renderIndent : function(){
37010         if(this.rendered){
37011             var indent = "";
37012             var p = this.node.parentNode;
37013             if(p){
37014                 indent = p.ui.getChildIndent();
37015             }
37016             if(this.indentMarkup != indent){ // don't rerender if not required
37017                 this.indentNode.innerHTML = indent;
37018                 this.indentMarkup = indent;
37019             }
37020             this.updateExpandIcon();
37021         }
37022     }
37023 };
37024
37025 Roo.tree.RootTreeNodeUI = function(){
37026     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37027 };
37028 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37029     render : function(){
37030         if(!this.rendered){
37031             var targetNode = this.node.ownerTree.innerCt.dom;
37032             this.node.expanded = true;
37033             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37034             this.wrap = this.ctNode = targetNode.firstChild;
37035         }
37036     },
37037     collapse : function(){
37038     },
37039     expand : function(){
37040     }
37041 });/*
37042  * Based on:
37043  * Ext JS Library 1.1.1
37044  * Copyright(c) 2006-2007, Ext JS, LLC.
37045  *
37046  * Originally Released Under LGPL - original licence link has changed is not relivant.
37047  *
37048  * Fork - LGPL
37049  * <script type="text/javascript">
37050  */
37051 /**
37052  * @class Roo.tree.TreeLoader
37053  * @extends Roo.util.Observable
37054  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37055  * nodes from a specified URL. The response must be a javascript Array definition
37056  * who's elements are node definition objects. eg:
37057  * <pre><code>
37058 {  success : true,
37059    data :      [
37060    
37061     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37062     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37063     ]
37064 }
37065
37066
37067 </code></pre>
37068  * <br><br>
37069  * The old style respose with just an array is still supported, but not recommended.
37070  * <br><br>
37071  *
37072  * A server request is sent, and child nodes are loaded only when a node is expanded.
37073  * The loading node's id is passed to the server under the parameter name "node" to
37074  * enable the server to produce the correct child nodes.
37075  * <br><br>
37076  * To pass extra parameters, an event handler may be attached to the "beforeload"
37077  * event, and the parameters specified in the TreeLoader's baseParams property:
37078  * <pre><code>
37079     myTreeLoader.on("beforeload", function(treeLoader, node) {
37080         this.baseParams.category = node.attributes.category;
37081     }, this);
37082     
37083 </code></pre>
37084  *
37085  * This would pass an HTTP parameter called "category" to the server containing
37086  * the value of the Node's "category" attribute.
37087  * @constructor
37088  * Creates a new Treeloader.
37089  * @param {Object} config A config object containing config properties.
37090  */
37091 Roo.tree.TreeLoader = function(config){
37092     this.baseParams = {};
37093     this.requestMethod = "POST";
37094     Roo.apply(this, config);
37095
37096     this.addEvents({
37097     
37098         /**
37099          * @event beforeload
37100          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37101          * @param {Object} This TreeLoader object.
37102          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37103          * @param {Object} callback The callback function specified in the {@link #load} call.
37104          */
37105         beforeload : true,
37106         /**
37107          * @event load
37108          * Fires when the node has been successfuly loaded.
37109          * @param {Object} This TreeLoader object.
37110          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37111          * @param {Object} response The response object containing the data from the server.
37112          */
37113         load : true,
37114         /**
37115          * @event loadexception
37116          * Fires if the network request failed.
37117          * @param {Object} This TreeLoader object.
37118          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37119          * @param {Object} response The response object containing the data from the server.
37120          */
37121         loadexception : true,
37122         /**
37123          * @event create
37124          * Fires before a node is created, enabling you to return custom Node types 
37125          * @param {Object} This TreeLoader object.
37126          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37127          */
37128         create : true
37129     });
37130
37131     Roo.tree.TreeLoader.superclass.constructor.call(this);
37132 };
37133
37134 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37135     /**
37136     * @cfg {String} dataUrl The URL from which to request a Json string which
37137     * specifies an array of node definition object representing the child nodes
37138     * to be loaded.
37139     */
37140     /**
37141     * @cfg {String} requestMethod either GET or POST
37142     * defaults to POST (due to BC)
37143     * to be loaded.
37144     */
37145     /**
37146     * @cfg {Object} baseParams (optional) An object containing properties which
37147     * specify HTTP parameters to be passed to each request for child nodes.
37148     */
37149     /**
37150     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37151     * created by this loader. If the attributes sent by the server have an attribute in this object,
37152     * they take priority.
37153     */
37154     /**
37155     * @cfg {Object} uiProviders (optional) An object containing properties which
37156     * 
37157     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37158     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37159     * <i>uiProvider</i> attribute of a returned child node is a string rather
37160     * than a reference to a TreeNodeUI implementation, this that string value
37161     * is used as a property name in the uiProviders object. You can define the provider named
37162     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37163     */
37164     uiProviders : {},
37165
37166     /**
37167     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37168     * child nodes before loading.
37169     */
37170     clearOnLoad : true,
37171
37172     /**
37173     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37174     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37175     * Grid query { data : [ .....] }
37176     */
37177     
37178     root : false,
37179      /**
37180     * @cfg {String} queryParam (optional) 
37181     * Name of the query as it will be passed on the querystring (defaults to 'node')
37182     * eg. the request will be ?node=[id]
37183     */
37184     
37185     
37186     queryParam: false,
37187     
37188     /**
37189      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37190      * This is called automatically when a node is expanded, but may be used to reload
37191      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37192      * @param {Roo.tree.TreeNode} node
37193      * @param {Function} callback
37194      */
37195     load : function(node, callback){
37196         if(this.clearOnLoad){
37197             while(node.firstChild){
37198                 node.removeChild(node.firstChild);
37199             }
37200         }
37201         if(node.attributes.children){ // preloaded json children
37202             var cs = node.attributes.children;
37203             for(var i = 0, len = cs.length; i < len; i++){
37204                 node.appendChild(this.createNode(cs[i]));
37205             }
37206             if(typeof callback == "function"){
37207                 callback();
37208             }
37209         }else if(this.dataUrl){
37210             this.requestData(node, callback);
37211         }
37212     },
37213
37214     getParams: function(node){
37215         var buf = [], bp = this.baseParams;
37216         for(var key in bp){
37217             if(typeof bp[key] != "function"){
37218                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37219             }
37220         }
37221         var n = this.queryParam === false ? 'node' : this.queryParam;
37222         buf.push(n + "=", encodeURIComponent(node.id));
37223         return buf.join("");
37224     },
37225
37226     requestData : function(node, callback){
37227         if(this.fireEvent("beforeload", this, node, callback) !== false){
37228             this.transId = Roo.Ajax.request({
37229                 method:this.requestMethod,
37230                 url: this.dataUrl||this.url,
37231                 success: this.handleResponse,
37232                 failure: this.handleFailure,
37233                 scope: this,
37234                 argument: {callback: callback, node: node},
37235                 params: this.getParams(node)
37236             });
37237         }else{
37238             // if the load is cancelled, make sure we notify
37239             // the node that we are done
37240             if(typeof callback == "function"){
37241                 callback();
37242             }
37243         }
37244     },
37245
37246     isLoading : function(){
37247         return this.transId ? true : false;
37248     },
37249
37250     abort : function(){
37251         if(this.isLoading()){
37252             Roo.Ajax.abort(this.transId);
37253         }
37254     },
37255
37256     // private
37257     createNode : function(attr)
37258     {
37259         // apply baseAttrs, nice idea Corey!
37260         if(this.baseAttrs){
37261             Roo.applyIf(attr, this.baseAttrs);
37262         }
37263         if(this.applyLoader !== false){
37264             attr.loader = this;
37265         }
37266         // uiProvider = depreciated..
37267         
37268         if(typeof(attr.uiProvider) == 'string'){
37269            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37270                 /**  eval:var:attr */ eval(attr.uiProvider);
37271         }
37272         if(typeof(this.uiProviders['default']) != 'undefined') {
37273             attr.uiProvider = this.uiProviders['default'];
37274         }
37275         
37276         this.fireEvent('create', this, attr);
37277         
37278         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37279         return(attr.leaf ?
37280                         new Roo.tree.TreeNode(attr) :
37281                         new Roo.tree.AsyncTreeNode(attr));
37282     },
37283
37284     processResponse : function(response, node, callback)
37285     {
37286         var json = response.responseText;
37287         try {
37288             
37289             var o = Roo.decode(json);
37290             
37291             if (this.root === false && typeof(o.success) != undefined) {
37292                 this.root = 'data'; // the default behaviour for list like data..
37293                 }
37294                 
37295             if (this.root !== false &&  !o.success) {
37296                 // it's a failure condition.
37297                 var a = response.argument;
37298                 this.fireEvent("loadexception", this, a.node, response);
37299                 Roo.log("Load failed - should have a handler really");
37300                 return;
37301             }
37302             
37303             
37304             
37305             if (this.root !== false) {
37306                  o = o[this.root];
37307             }
37308             
37309             for(var i = 0, len = o.length; i < len; i++){
37310                 var n = this.createNode(o[i]);
37311                 if(n){
37312                     node.appendChild(n);
37313                 }
37314             }
37315             if(typeof callback == "function"){
37316                 callback(this, node);
37317             }
37318         }catch(e){
37319             this.handleFailure(response);
37320         }
37321     },
37322
37323     handleResponse : function(response){
37324         this.transId = false;
37325         var a = response.argument;
37326         this.processResponse(response, a.node, a.callback);
37327         this.fireEvent("load", this, a.node, response);
37328     },
37329
37330     handleFailure : function(response)
37331     {
37332         // should handle failure better..
37333         this.transId = false;
37334         var a = response.argument;
37335         this.fireEvent("loadexception", this, a.node, response);
37336         if(typeof a.callback == "function"){
37337             a.callback(this, a.node);
37338         }
37339     }
37340 });/*
37341  * Based on:
37342  * Ext JS Library 1.1.1
37343  * Copyright(c) 2006-2007, Ext JS, LLC.
37344  *
37345  * Originally Released Under LGPL - original licence link has changed is not relivant.
37346  *
37347  * Fork - LGPL
37348  * <script type="text/javascript">
37349  */
37350
37351 /**
37352 * @class Roo.tree.TreeFilter
37353 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37354 * @param {TreePanel} tree
37355 * @param {Object} config (optional)
37356  */
37357 Roo.tree.TreeFilter = function(tree, config){
37358     this.tree = tree;
37359     this.filtered = {};
37360     Roo.apply(this, config);
37361 };
37362
37363 Roo.tree.TreeFilter.prototype = {
37364     clearBlank:false,
37365     reverse:false,
37366     autoClear:false,
37367     remove:false,
37368
37369      /**
37370      * Filter the data by a specific attribute.
37371      * @param {String/RegExp} value Either string that the attribute value
37372      * should start with or a RegExp to test against the attribute
37373      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37374      * @param {TreeNode} startNode (optional) The node to start the filter at.
37375      */
37376     filter : function(value, attr, startNode){
37377         attr = attr || "text";
37378         var f;
37379         if(typeof value == "string"){
37380             var vlen = value.length;
37381             // auto clear empty filter
37382             if(vlen == 0 && this.clearBlank){
37383                 this.clear();
37384                 return;
37385             }
37386             value = value.toLowerCase();
37387             f = function(n){
37388                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37389             };
37390         }else if(value.exec){ // regex?
37391             f = function(n){
37392                 return value.test(n.attributes[attr]);
37393             };
37394         }else{
37395             throw 'Illegal filter type, must be string or regex';
37396         }
37397         this.filterBy(f, null, startNode);
37398         },
37399
37400     /**
37401      * Filter by a function. The passed function will be called with each
37402      * node in the tree (or from the startNode). If the function returns true, the node is kept
37403      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37404      * @param {Function} fn The filter function
37405      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37406      */
37407     filterBy : function(fn, scope, startNode){
37408         startNode = startNode || this.tree.root;
37409         if(this.autoClear){
37410             this.clear();
37411         }
37412         var af = this.filtered, rv = this.reverse;
37413         var f = function(n){
37414             if(n == startNode){
37415                 return true;
37416             }
37417             if(af[n.id]){
37418                 return false;
37419             }
37420             var m = fn.call(scope || n, n);
37421             if(!m || rv){
37422                 af[n.id] = n;
37423                 n.ui.hide();
37424                 return false;
37425             }
37426             return true;
37427         };
37428         startNode.cascade(f);
37429         if(this.remove){
37430            for(var id in af){
37431                if(typeof id != "function"){
37432                    var n = af[id];
37433                    if(n && n.parentNode){
37434                        n.parentNode.removeChild(n);
37435                    }
37436                }
37437            }
37438         }
37439     },
37440
37441     /**
37442      * Clears the current filter. Note: with the "remove" option
37443      * set a filter cannot be cleared.
37444      */
37445     clear : function(){
37446         var t = this.tree;
37447         var af = this.filtered;
37448         for(var id in af){
37449             if(typeof id != "function"){
37450                 var n = af[id];
37451                 if(n){
37452                     n.ui.show();
37453                 }
37454             }
37455         }
37456         this.filtered = {};
37457     }
37458 };
37459 /*
37460  * Based on:
37461  * Ext JS Library 1.1.1
37462  * Copyright(c) 2006-2007, Ext JS, LLC.
37463  *
37464  * Originally Released Under LGPL - original licence link has changed is not relivant.
37465  *
37466  * Fork - LGPL
37467  * <script type="text/javascript">
37468  */
37469  
37470
37471 /**
37472  * @class Roo.tree.TreeSorter
37473  * Provides sorting of nodes in a TreePanel
37474  * 
37475  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37476  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37477  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37478  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37479  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37480  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37481  * @constructor
37482  * @param {TreePanel} tree
37483  * @param {Object} config
37484  */
37485 Roo.tree.TreeSorter = function(tree, config){
37486     Roo.apply(this, config);
37487     tree.on("beforechildrenrendered", this.doSort, this);
37488     tree.on("append", this.updateSort, this);
37489     tree.on("insert", this.updateSort, this);
37490     
37491     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37492     var p = this.property || "text";
37493     var sortType = this.sortType;
37494     var fs = this.folderSort;
37495     var cs = this.caseSensitive === true;
37496     var leafAttr = this.leafAttr || 'leaf';
37497
37498     this.sortFn = function(n1, n2){
37499         if(fs){
37500             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37501                 return 1;
37502             }
37503             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37504                 return -1;
37505             }
37506         }
37507         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37508         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37509         if(v1 < v2){
37510                         return dsc ? +1 : -1;
37511                 }else if(v1 > v2){
37512                         return dsc ? -1 : +1;
37513         }else{
37514                 return 0;
37515         }
37516     };
37517 };
37518
37519 Roo.tree.TreeSorter.prototype = {
37520     doSort : function(node){
37521         node.sort(this.sortFn);
37522     },
37523     
37524     compareNodes : function(n1, n2){
37525         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37526     },
37527     
37528     updateSort : function(tree, node){
37529         if(node.childrenRendered){
37530             this.doSort.defer(1, this, [node]);
37531         }
37532     }
37533 };/*
37534  * Based on:
37535  * Ext JS Library 1.1.1
37536  * Copyright(c) 2006-2007, Ext JS, LLC.
37537  *
37538  * Originally Released Under LGPL - original licence link has changed is not relivant.
37539  *
37540  * Fork - LGPL
37541  * <script type="text/javascript">
37542  */
37543
37544 if(Roo.dd.DropZone){
37545     
37546 Roo.tree.TreeDropZone = function(tree, config){
37547     this.allowParentInsert = false;
37548     this.allowContainerDrop = false;
37549     this.appendOnly = false;
37550     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37551     this.tree = tree;
37552     this.lastInsertClass = "x-tree-no-status";
37553     this.dragOverData = {};
37554 };
37555
37556 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37557     ddGroup : "TreeDD",
37558     scroll:  true,
37559     
37560     expandDelay : 1000,
37561     
37562     expandNode : function(node){
37563         if(node.hasChildNodes() && !node.isExpanded()){
37564             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37565         }
37566     },
37567     
37568     queueExpand : function(node){
37569         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37570     },
37571     
37572     cancelExpand : function(){
37573         if(this.expandProcId){
37574             clearTimeout(this.expandProcId);
37575             this.expandProcId = false;
37576         }
37577     },
37578     
37579     isValidDropPoint : function(n, pt, dd, e, data){
37580         if(!n || !data){ return false; }
37581         var targetNode = n.node;
37582         var dropNode = data.node;
37583         // default drop rules
37584         if(!(targetNode && targetNode.isTarget && pt)){
37585             return false;
37586         }
37587         if(pt == "append" && targetNode.allowChildren === false){
37588             return false;
37589         }
37590         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37591             return false;
37592         }
37593         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37594             return false;
37595         }
37596         // reuse the object
37597         var overEvent = this.dragOverData;
37598         overEvent.tree = this.tree;
37599         overEvent.target = targetNode;
37600         overEvent.data = data;
37601         overEvent.point = pt;
37602         overEvent.source = dd;
37603         overEvent.rawEvent = e;
37604         overEvent.dropNode = dropNode;
37605         overEvent.cancel = false;  
37606         var result = this.tree.fireEvent("nodedragover", overEvent);
37607         return overEvent.cancel === false && result !== false;
37608     },
37609     
37610     getDropPoint : function(e, n, dd)
37611     {
37612         var tn = n.node;
37613         if(tn.isRoot){
37614             return tn.allowChildren !== false ? "append" : false; // always append for root
37615         }
37616         var dragEl = n.ddel;
37617         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37618         var y = Roo.lib.Event.getPageY(e);
37619         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37620         
37621         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37622         var noAppend = tn.allowChildren === false;
37623         if(this.appendOnly || tn.parentNode.allowChildren === false){
37624             return noAppend ? false : "append";
37625         }
37626         var noBelow = false;
37627         if(!this.allowParentInsert){
37628             noBelow = tn.hasChildNodes() && tn.isExpanded();
37629         }
37630         var q = (b - t) / (noAppend ? 2 : 3);
37631         if(y >= t && y < (t + q)){
37632             return "above";
37633         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37634             return "below";
37635         }else{
37636             return "append";
37637         }
37638     },
37639     
37640     onNodeEnter : function(n, dd, e, data)
37641     {
37642         this.cancelExpand();
37643     },
37644     
37645     onNodeOver : function(n, dd, e, data)
37646     {
37647        
37648         var pt = this.getDropPoint(e, n, dd);
37649         var node = n.node;
37650         
37651         // auto node expand check
37652         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37653             this.queueExpand(node);
37654         }else if(pt != "append"){
37655             this.cancelExpand();
37656         }
37657         
37658         // set the insert point style on the target node
37659         var returnCls = this.dropNotAllowed;
37660         if(this.isValidDropPoint(n, pt, dd, e, data)){
37661            if(pt){
37662                var el = n.ddel;
37663                var cls;
37664                if(pt == "above"){
37665                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37666                    cls = "x-tree-drag-insert-above";
37667                }else if(pt == "below"){
37668                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37669                    cls = "x-tree-drag-insert-below";
37670                }else{
37671                    returnCls = "x-tree-drop-ok-append";
37672                    cls = "x-tree-drag-append";
37673                }
37674                if(this.lastInsertClass != cls){
37675                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37676                    this.lastInsertClass = cls;
37677                }
37678            }
37679        }
37680        return returnCls;
37681     },
37682     
37683     onNodeOut : function(n, dd, e, data){
37684         
37685         this.cancelExpand();
37686         this.removeDropIndicators(n);
37687     },
37688     
37689     onNodeDrop : function(n, dd, e, data){
37690         var point = this.getDropPoint(e, n, dd);
37691         var targetNode = n.node;
37692         targetNode.ui.startDrop();
37693         if(!this.isValidDropPoint(n, point, dd, e, data)){
37694             targetNode.ui.endDrop();
37695             return false;
37696         }
37697         // first try to find the drop node
37698         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37699         var dropEvent = {
37700             tree : this.tree,
37701             target: targetNode,
37702             data: data,
37703             point: point,
37704             source: dd,
37705             rawEvent: e,
37706             dropNode: dropNode,
37707             cancel: !dropNode   
37708         };
37709         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37710         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37711             targetNode.ui.endDrop();
37712             return false;
37713         }
37714         // allow target changing
37715         targetNode = dropEvent.target;
37716         if(point == "append" && !targetNode.isExpanded()){
37717             targetNode.expand(false, null, function(){
37718                 this.completeDrop(dropEvent);
37719             }.createDelegate(this));
37720         }else{
37721             this.completeDrop(dropEvent);
37722         }
37723         return true;
37724     },
37725     
37726     completeDrop : function(de){
37727         var ns = de.dropNode, p = de.point, t = de.target;
37728         if(!(ns instanceof Array)){
37729             ns = [ns];
37730         }
37731         var n;
37732         for(var i = 0, len = ns.length; i < len; i++){
37733             n = ns[i];
37734             if(p == "above"){
37735                 t.parentNode.insertBefore(n, t);
37736             }else if(p == "below"){
37737                 t.parentNode.insertBefore(n, t.nextSibling);
37738             }else{
37739                 t.appendChild(n);
37740             }
37741         }
37742         n.ui.focus();
37743         if(this.tree.hlDrop){
37744             n.ui.highlight();
37745         }
37746         t.ui.endDrop();
37747         this.tree.fireEvent("nodedrop", de);
37748     },
37749     
37750     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37751         if(this.tree.hlDrop){
37752             dropNode.ui.focus();
37753             dropNode.ui.highlight();
37754         }
37755         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37756     },
37757     
37758     getTree : function(){
37759         return this.tree;
37760     },
37761     
37762     removeDropIndicators : function(n){
37763         if(n && n.ddel){
37764             var el = n.ddel;
37765             Roo.fly(el).removeClass([
37766                     "x-tree-drag-insert-above",
37767                     "x-tree-drag-insert-below",
37768                     "x-tree-drag-append"]);
37769             this.lastInsertClass = "_noclass";
37770         }
37771     },
37772     
37773     beforeDragDrop : function(target, e, id){
37774         this.cancelExpand();
37775         return true;
37776     },
37777     
37778     afterRepair : function(data){
37779         if(data && Roo.enableFx){
37780             data.node.ui.highlight();
37781         }
37782         this.hideProxy();
37783     } 
37784     
37785 });
37786
37787 }
37788 /*
37789  * Based on:
37790  * Ext JS Library 1.1.1
37791  * Copyright(c) 2006-2007, Ext JS, LLC.
37792  *
37793  * Originally Released Under LGPL - original licence link has changed is not relivant.
37794  *
37795  * Fork - LGPL
37796  * <script type="text/javascript">
37797  */
37798  
37799
37800 if(Roo.dd.DragZone){
37801 Roo.tree.TreeDragZone = function(tree, config){
37802     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37803     this.tree = tree;
37804 };
37805
37806 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37807     ddGroup : "TreeDD",
37808    
37809     onBeforeDrag : function(data, e){
37810         var n = data.node;
37811         return n && n.draggable && !n.disabled;
37812     },
37813      
37814     
37815     onInitDrag : function(e){
37816         var data = this.dragData;
37817         this.tree.getSelectionModel().select(data.node);
37818         this.proxy.update("");
37819         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37820         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37821     },
37822     
37823     getRepairXY : function(e, data){
37824         return data.node.ui.getDDRepairXY();
37825     },
37826     
37827     onEndDrag : function(data, e){
37828         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37829         
37830         
37831     },
37832     
37833     onValidDrop : function(dd, e, id){
37834         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37835         this.hideProxy();
37836     },
37837     
37838     beforeInvalidDrop : function(e, id){
37839         // this scrolls the original position back into view
37840         var sm = this.tree.getSelectionModel();
37841         sm.clearSelections();
37842         sm.select(this.dragData.node);
37843     }
37844 });
37845 }/*
37846  * Based on:
37847  * Ext JS Library 1.1.1
37848  * Copyright(c) 2006-2007, Ext JS, LLC.
37849  *
37850  * Originally Released Under LGPL - original licence link has changed is not relivant.
37851  *
37852  * Fork - LGPL
37853  * <script type="text/javascript">
37854  */
37855 /**
37856  * @class Roo.tree.TreeEditor
37857  * @extends Roo.Editor
37858  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37859  * as the editor field.
37860  * @constructor
37861  * @param {Object} config (used to be the tree panel.)
37862  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37863  * 
37864  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37865  * @cfg {Roo.form.TextField} field [required] The field configuration
37866  *
37867  * 
37868  */
37869 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37870     var tree = config;
37871     var field;
37872     if (oldconfig) { // old style..
37873         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37874     } else {
37875         // new style..
37876         tree = config.tree;
37877         config.field = config.field  || {};
37878         config.field.xtype = 'TextField';
37879         field = Roo.factory(config.field, Roo.form);
37880     }
37881     config = config || {};
37882     
37883     
37884     this.addEvents({
37885         /**
37886          * @event beforenodeedit
37887          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37888          * false from the handler of this event.
37889          * @param {Editor} this
37890          * @param {Roo.tree.Node} node 
37891          */
37892         "beforenodeedit" : true
37893     });
37894     
37895     //Roo.log(config);
37896     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37897
37898     this.tree = tree;
37899
37900     tree.on('beforeclick', this.beforeNodeClick, this);
37901     tree.getTreeEl().on('mousedown', this.hide, this);
37902     this.on('complete', this.updateNode, this);
37903     this.on('beforestartedit', this.fitToTree, this);
37904     this.on('startedit', this.bindScroll, this, {delay:10});
37905     this.on('specialkey', this.onSpecialKey, this);
37906 };
37907
37908 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37909     /**
37910      * @cfg {String} alignment
37911      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37912      */
37913     alignment: "l-l",
37914     // inherit
37915     autoSize: false,
37916     /**
37917      * @cfg {Boolean} hideEl
37918      * True to hide the bound element while the editor is displayed (defaults to false)
37919      */
37920     hideEl : false,
37921     /**
37922      * @cfg {String} cls
37923      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37924      */
37925     cls: "x-small-editor x-tree-editor",
37926     /**
37927      * @cfg {Boolean} shim
37928      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37929      */
37930     shim:false,
37931     // inherit
37932     shadow:"frame",
37933     /**
37934      * @cfg {Number} maxWidth
37935      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37936      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37937      * scroll and client offsets into account prior to each edit.
37938      */
37939     maxWidth: 250,
37940
37941     editDelay : 350,
37942
37943     // private
37944     fitToTree : function(ed, el){
37945         var td = this.tree.getTreeEl().dom, nd = el.dom;
37946         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37947             td.scrollLeft = nd.offsetLeft;
37948         }
37949         var w = Math.min(
37950                 this.maxWidth,
37951                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37952         this.setSize(w, '');
37953         
37954         return this.fireEvent('beforenodeedit', this, this.editNode);
37955         
37956     },
37957
37958     // private
37959     triggerEdit : function(node){
37960         this.completeEdit();
37961         this.editNode = node;
37962         this.startEdit(node.ui.textNode, node.text);
37963     },
37964
37965     // private
37966     bindScroll : function(){
37967         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37968     },
37969
37970     // private
37971     beforeNodeClick : function(node, e){
37972         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37973         this.lastClick = new Date();
37974         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37975             e.stopEvent();
37976             this.triggerEdit(node);
37977             return false;
37978         }
37979         return true;
37980     },
37981
37982     // private
37983     updateNode : function(ed, value){
37984         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37985         this.editNode.setText(value);
37986     },
37987
37988     // private
37989     onHide : function(){
37990         Roo.tree.TreeEditor.superclass.onHide.call(this);
37991         if(this.editNode){
37992             this.editNode.ui.focus();
37993         }
37994     },
37995
37996     // private
37997     onSpecialKey : function(field, e){
37998         var k = e.getKey();
37999         if(k == e.ESC){
38000             e.stopEvent();
38001             this.cancelEdit();
38002         }else if(k == e.ENTER && !e.hasModifier()){
38003             e.stopEvent();
38004             this.completeEdit();
38005         }
38006     }
38007 });//<Script type="text/javascript">
38008 /*
38009  * Based on:
38010  * Ext JS Library 1.1.1
38011  * Copyright(c) 2006-2007, Ext JS, LLC.
38012  *
38013  * Originally Released Under LGPL - original licence link has changed is not relivant.
38014  *
38015  * Fork - LGPL
38016  * <script type="text/javascript">
38017  */
38018  
38019 /**
38020  * Not documented??? - probably should be...
38021  */
38022
38023 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38024     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38025     
38026     renderElements : function(n, a, targetNode, bulkRender){
38027         //consel.log("renderElements?");
38028         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38029
38030         var t = n.getOwnerTree();
38031         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38032         
38033         var cols = t.columns;
38034         var bw = t.borderWidth;
38035         var c = cols[0];
38036         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38037          var cb = typeof a.checked == "boolean";
38038         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38039         var colcls = 'x-t-' + tid + '-c0';
38040         var buf = [
38041             '<li class="x-tree-node">',
38042             
38043                 
38044                 '<div class="x-tree-node-el ', a.cls,'">',
38045                     // extran...
38046                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38047                 
38048                 
38049                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38050                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38051                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38052                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38053                            (a.iconCls ? ' '+a.iconCls : ''),
38054                            '" unselectable="on" />',
38055                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38056                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38057                              
38058                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38059                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38060                             '<span unselectable="on" qtip="' + tx + '">',
38061                              tx,
38062                              '</span></a>' ,
38063                     '</div>',
38064                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38065                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38066                  ];
38067         for(var i = 1, len = cols.length; i < len; i++){
38068             c = cols[i];
38069             colcls = 'x-t-' + tid + '-c' +i;
38070             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38071             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38072                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38073                       "</div>");
38074          }
38075          
38076          buf.push(
38077             '</a>',
38078             '<div class="x-clear"></div></div>',
38079             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38080             "</li>");
38081         
38082         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38083             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38084                                 n.nextSibling.ui.getEl(), buf.join(""));
38085         }else{
38086             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38087         }
38088         var el = this.wrap.firstChild;
38089         this.elRow = el;
38090         this.elNode = el.firstChild;
38091         this.ranchor = el.childNodes[1];
38092         this.ctNode = this.wrap.childNodes[1];
38093         var cs = el.firstChild.childNodes;
38094         this.indentNode = cs[0];
38095         this.ecNode = cs[1];
38096         this.iconNode = cs[2];
38097         var index = 3;
38098         if(cb){
38099             this.checkbox = cs[3];
38100             index++;
38101         }
38102         this.anchor = cs[index];
38103         
38104         this.textNode = cs[index].firstChild;
38105         
38106         //el.on("click", this.onClick, this);
38107         //el.on("dblclick", this.onDblClick, this);
38108         
38109         
38110        // console.log(this);
38111     },
38112     initEvents : function(){
38113         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38114         
38115             
38116         var a = this.ranchor;
38117
38118         var el = Roo.get(a);
38119
38120         if(Roo.isOpera){ // opera render bug ignores the CSS
38121             el.setStyle("text-decoration", "none");
38122         }
38123
38124         el.on("click", this.onClick, this);
38125         el.on("dblclick", this.onDblClick, this);
38126         el.on("contextmenu", this.onContextMenu, this);
38127         
38128     },
38129     
38130     /*onSelectedChange : function(state){
38131         if(state){
38132             this.focus();
38133             this.addClass("x-tree-selected");
38134         }else{
38135             //this.blur();
38136             this.removeClass("x-tree-selected");
38137         }
38138     },*/
38139     addClass : function(cls){
38140         if(this.elRow){
38141             Roo.fly(this.elRow).addClass(cls);
38142         }
38143         
38144     },
38145     
38146     
38147     removeClass : function(cls){
38148         if(this.elRow){
38149             Roo.fly(this.elRow).removeClass(cls);
38150         }
38151     }
38152
38153     
38154     
38155 });//<Script type="text/javascript">
38156
38157 /*
38158  * Based on:
38159  * Ext JS Library 1.1.1
38160  * Copyright(c) 2006-2007, Ext JS, LLC.
38161  *
38162  * Originally Released Under LGPL - original licence link has changed is not relivant.
38163  *
38164  * Fork - LGPL
38165  * <script type="text/javascript">
38166  */
38167  
38168
38169 /**
38170  * @class Roo.tree.ColumnTree
38171  * @extends Roo.tree.TreePanel
38172  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38173  * @cfg {int} borderWidth  compined right/left border allowance
38174  * @constructor
38175  * @param {String/HTMLElement/Element} el The container element
38176  * @param {Object} config
38177  */
38178 Roo.tree.ColumnTree =  function(el, config)
38179 {
38180    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38181    this.addEvents({
38182         /**
38183         * @event resize
38184         * Fire this event on a container when it resizes
38185         * @param {int} w Width
38186         * @param {int} h Height
38187         */
38188        "resize" : true
38189     });
38190     this.on('resize', this.onResize, this);
38191 };
38192
38193 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38194     //lines:false,
38195     
38196     
38197     borderWidth: Roo.isBorderBox ? 0 : 2, 
38198     headEls : false,
38199     
38200     render : function(){
38201         // add the header.....
38202        
38203         Roo.tree.ColumnTree.superclass.render.apply(this);
38204         
38205         this.el.addClass('x-column-tree');
38206         
38207         this.headers = this.el.createChild(
38208             {cls:'x-tree-headers'},this.innerCt.dom);
38209    
38210         var cols = this.columns, c;
38211         var totalWidth = 0;
38212         this.headEls = [];
38213         var  len = cols.length;
38214         for(var i = 0; i < len; i++){
38215              c = cols[i];
38216              totalWidth += c.width;
38217             this.headEls.push(this.headers.createChild({
38218                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38219                  cn: {
38220                      cls:'x-tree-hd-text',
38221                      html: c.header
38222                  },
38223                  style:'width:'+(c.width-this.borderWidth)+'px;'
38224              }));
38225         }
38226         this.headers.createChild({cls:'x-clear'});
38227         // prevent floats from wrapping when clipped
38228         this.headers.setWidth(totalWidth);
38229         //this.innerCt.setWidth(totalWidth);
38230         this.innerCt.setStyle({ overflow: 'auto' });
38231         this.onResize(this.width, this.height);
38232              
38233         
38234     },
38235     onResize : function(w,h)
38236     {
38237         this.height = h;
38238         this.width = w;
38239         // resize cols..
38240         this.innerCt.setWidth(this.width);
38241         this.innerCt.setHeight(this.height-20);
38242         
38243         // headers...
38244         var cols = this.columns, c;
38245         var totalWidth = 0;
38246         var expEl = false;
38247         var len = cols.length;
38248         for(var i = 0; i < len; i++){
38249             c = cols[i];
38250             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38251                 // it's the expander..
38252                 expEl  = this.headEls[i];
38253                 continue;
38254             }
38255             totalWidth += c.width;
38256             
38257         }
38258         if (expEl) {
38259             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38260         }
38261         this.headers.setWidth(w-20);
38262
38263         
38264         
38265         
38266     }
38267 });
38268 /*
38269  * Based on:
38270  * Ext JS Library 1.1.1
38271  * Copyright(c) 2006-2007, Ext JS, LLC.
38272  *
38273  * Originally Released Under LGPL - original licence link has changed is not relivant.
38274  *
38275  * Fork - LGPL
38276  * <script type="text/javascript">
38277  */
38278  
38279 /**
38280  * @class Roo.menu.Menu
38281  * @extends Roo.util.Observable
38282  * @children Roo.menu.BaseItem
38283  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38284  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38285  * @constructor
38286  * Creates a new Menu
38287  * @param {Object} config Configuration options
38288  */
38289 Roo.menu.Menu = function(config){
38290     
38291     Roo.menu.Menu.superclass.constructor.call(this, config);
38292     
38293     this.id = this.id || Roo.id();
38294     this.addEvents({
38295         /**
38296          * @event beforeshow
38297          * Fires before this menu is displayed
38298          * @param {Roo.menu.Menu} this
38299          */
38300         beforeshow : true,
38301         /**
38302          * @event beforehide
38303          * Fires before this menu is hidden
38304          * @param {Roo.menu.Menu} this
38305          */
38306         beforehide : true,
38307         /**
38308          * @event show
38309          * Fires after this menu is displayed
38310          * @param {Roo.menu.Menu} this
38311          */
38312         show : true,
38313         /**
38314          * @event hide
38315          * Fires after this menu is hidden
38316          * @param {Roo.menu.Menu} this
38317          */
38318         hide : true,
38319         /**
38320          * @event click
38321          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38322          * @param {Roo.menu.Menu} this
38323          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38324          * @param {Roo.EventObject} e
38325          */
38326         click : true,
38327         /**
38328          * @event mouseover
38329          * Fires when the mouse is hovering over this menu
38330          * @param {Roo.menu.Menu} this
38331          * @param {Roo.EventObject} e
38332          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38333          */
38334         mouseover : true,
38335         /**
38336          * @event mouseout
38337          * Fires when the mouse exits this menu
38338          * @param {Roo.menu.Menu} this
38339          * @param {Roo.EventObject} e
38340          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38341          */
38342         mouseout : true,
38343         /**
38344          * @event itemclick
38345          * Fires when a menu item contained in this menu is clicked
38346          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38347          * @param {Roo.EventObject} e
38348          */
38349         itemclick: true
38350     });
38351     if (this.registerMenu) {
38352         Roo.menu.MenuMgr.register(this);
38353     }
38354     
38355     var mis = this.items;
38356     this.items = new Roo.util.MixedCollection();
38357     if(mis){
38358         this.add.apply(this, mis);
38359     }
38360 };
38361
38362 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38363     /**
38364      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38365      */
38366     minWidth : 120,
38367     /**
38368      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38369      * for bottom-right shadow (defaults to "sides")
38370      */
38371     shadow : "sides",
38372     /**
38373      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38374      * this menu (defaults to "tl-tr?")
38375      */
38376     subMenuAlign : "tl-tr?",
38377     /**
38378      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38379      * relative to its element of origin (defaults to "tl-bl?")
38380      */
38381     defaultAlign : "tl-bl?",
38382     /**
38383      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38384      */
38385     allowOtherMenus : false,
38386     /**
38387      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38388      */
38389     registerMenu : true,
38390
38391     hidden:true,
38392
38393     // private
38394     render : function(){
38395         if(this.el){
38396             return;
38397         }
38398         var el = this.el = new Roo.Layer({
38399             cls: "x-menu",
38400             shadow:this.shadow,
38401             constrain: false,
38402             parentEl: this.parentEl || document.body,
38403             zindex:15000
38404         });
38405
38406         this.keyNav = new Roo.menu.MenuNav(this);
38407
38408         if(this.plain){
38409             el.addClass("x-menu-plain");
38410         }
38411         if(this.cls){
38412             el.addClass(this.cls);
38413         }
38414         // generic focus element
38415         this.focusEl = el.createChild({
38416             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38417         });
38418         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38419         //disabling touch- as it's causing issues ..
38420         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38421         ul.on('click'   , this.onClick, this);
38422         
38423         
38424         ul.on("mouseover", this.onMouseOver, this);
38425         ul.on("mouseout", this.onMouseOut, this);
38426         this.items.each(function(item){
38427             if (item.hidden) {
38428                 return;
38429             }
38430             
38431             var li = document.createElement("li");
38432             li.className = "x-menu-list-item";
38433             ul.dom.appendChild(li);
38434             item.render(li, this);
38435         }, this);
38436         this.ul = ul;
38437         this.autoWidth();
38438     },
38439
38440     // private
38441     autoWidth : function(){
38442         var el = this.el, ul = this.ul;
38443         if(!el){
38444             return;
38445         }
38446         var w = this.width;
38447         if(w){
38448             el.setWidth(w);
38449         }else if(Roo.isIE){
38450             el.setWidth(this.minWidth);
38451             var t = el.dom.offsetWidth; // force recalc
38452             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38453         }
38454     },
38455
38456     // private
38457     delayAutoWidth : function(){
38458         if(this.rendered){
38459             if(!this.awTask){
38460                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38461             }
38462             this.awTask.delay(20);
38463         }
38464     },
38465
38466     // private
38467     findTargetItem : function(e){
38468         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38469         if(t && t.menuItemId){
38470             return this.items.get(t.menuItemId);
38471         }
38472     },
38473
38474     // private
38475     onClick : function(e){
38476         Roo.log("menu.onClick");
38477         var t = this.findTargetItem(e);
38478         if(!t){
38479             return;
38480         }
38481         Roo.log(e);
38482         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38483             if(t == this.activeItem && t.shouldDeactivate(e)){
38484                 this.activeItem.deactivate();
38485                 delete this.activeItem;
38486                 return;
38487             }
38488             if(t.canActivate){
38489                 this.setActiveItem(t, true);
38490             }
38491             return;
38492             
38493             
38494         }
38495         
38496         t.onClick(e);
38497         this.fireEvent("click", this, t, e);
38498     },
38499
38500     // private
38501     setActiveItem : function(item, autoExpand){
38502         if(item != this.activeItem){
38503             if(this.activeItem){
38504                 this.activeItem.deactivate();
38505             }
38506             this.activeItem = item;
38507             item.activate(autoExpand);
38508         }else if(autoExpand){
38509             item.expandMenu();
38510         }
38511     },
38512
38513     // private
38514     tryActivate : function(start, step){
38515         var items = this.items;
38516         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38517             var item = items.get(i);
38518             if(!item.disabled && item.canActivate){
38519                 this.setActiveItem(item, false);
38520                 return item;
38521             }
38522         }
38523         return false;
38524     },
38525
38526     // private
38527     onMouseOver : function(e){
38528         var t;
38529         if(t = this.findTargetItem(e)){
38530             if(t.canActivate && !t.disabled){
38531                 this.setActiveItem(t, true);
38532             }
38533         }
38534         this.fireEvent("mouseover", this, e, t);
38535     },
38536
38537     // private
38538     onMouseOut : function(e){
38539         var t;
38540         if(t = this.findTargetItem(e)){
38541             if(t == this.activeItem && t.shouldDeactivate(e)){
38542                 this.activeItem.deactivate();
38543                 delete this.activeItem;
38544             }
38545         }
38546         this.fireEvent("mouseout", this, e, t);
38547     },
38548
38549     /**
38550      * Read-only.  Returns true if the menu is currently displayed, else false.
38551      * @type Boolean
38552      */
38553     isVisible : function(){
38554         return this.el && !this.hidden;
38555     },
38556
38557     /**
38558      * Displays this menu relative to another element
38559      * @param {String/HTMLElement/Roo.Element} element The element to align to
38560      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38561      * the element (defaults to this.defaultAlign)
38562      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38563      */
38564     show : function(el, pos, parentMenu){
38565         this.parentMenu = parentMenu;
38566         if(!this.el){
38567             this.render();
38568         }
38569         this.fireEvent("beforeshow", this);
38570         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38571     },
38572
38573     /**
38574      * Displays this menu at a specific xy position
38575      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38576      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38577      */
38578     showAt : function(xy, parentMenu, /* private: */_e){
38579         this.parentMenu = parentMenu;
38580         if(!this.el){
38581             this.render();
38582         }
38583         if(_e !== false){
38584             this.fireEvent("beforeshow", this);
38585             xy = this.el.adjustForConstraints(xy);
38586         }
38587         this.el.setXY(xy);
38588         this.el.show();
38589         this.hidden = false;
38590         this.focus();
38591         this.fireEvent("show", this);
38592     },
38593
38594     focus : function(){
38595         if(!this.hidden){
38596             this.doFocus.defer(50, this);
38597         }
38598     },
38599
38600     doFocus : function(){
38601         if(!this.hidden){
38602             this.focusEl.focus();
38603         }
38604     },
38605
38606     /**
38607      * Hides this menu and optionally all parent menus
38608      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38609      */
38610     hide : function(deep){
38611         if(this.el && this.isVisible()){
38612             this.fireEvent("beforehide", this);
38613             if(this.activeItem){
38614                 this.activeItem.deactivate();
38615                 this.activeItem = null;
38616             }
38617             this.el.hide();
38618             this.hidden = true;
38619             this.fireEvent("hide", this);
38620         }
38621         if(deep === true && this.parentMenu){
38622             this.parentMenu.hide(true);
38623         }
38624     },
38625
38626     /**
38627      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38628      * Any of the following are valid:
38629      * <ul>
38630      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38631      * <li>An HTMLElement object which will be converted to a menu item</li>
38632      * <li>A menu item config object that will be created as a new menu item</li>
38633      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38634      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38635      * </ul>
38636      * Usage:
38637      * <pre><code>
38638 // Create the menu
38639 var menu = new Roo.menu.Menu();
38640
38641 // Create a menu item to add by reference
38642 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38643
38644 // Add a bunch of items at once using different methods.
38645 // Only the last item added will be returned.
38646 var item = menu.add(
38647     menuItem,                // add existing item by ref
38648     'Dynamic Item',          // new TextItem
38649     '-',                     // new separator
38650     { text: 'Config Item' }  // new item by config
38651 );
38652 </code></pre>
38653      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38654      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38655      */
38656     add : function(){
38657         var a = arguments, l = a.length, item;
38658         for(var i = 0; i < l; i++){
38659             var el = a[i];
38660             if ((typeof(el) == "object") && el.xtype && el.xns) {
38661                 el = Roo.factory(el, Roo.menu);
38662             }
38663             
38664             if(el.render){ // some kind of Item
38665                 item = this.addItem(el);
38666             }else if(typeof el == "string"){ // string
38667                 if(el == "separator" || el == "-"){
38668                     item = this.addSeparator();
38669                 }else{
38670                     item = this.addText(el);
38671                 }
38672             }else if(el.tagName || el.el){ // element
38673                 item = this.addElement(el);
38674             }else if(typeof el == "object"){ // must be menu item config?
38675                 item = this.addMenuItem(el);
38676             }
38677         }
38678         return item;
38679     },
38680
38681     /**
38682      * Returns this menu's underlying {@link Roo.Element} object
38683      * @return {Roo.Element} The element
38684      */
38685     getEl : function(){
38686         if(!this.el){
38687             this.render();
38688         }
38689         return this.el;
38690     },
38691
38692     /**
38693      * Adds a separator bar to the menu
38694      * @return {Roo.menu.Item} The menu item that was added
38695      */
38696     addSeparator : function(){
38697         return this.addItem(new Roo.menu.Separator());
38698     },
38699
38700     /**
38701      * Adds an {@link Roo.Element} object to the menu
38702      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38703      * @return {Roo.menu.Item} The menu item that was added
38704      */
38705     addElement : function(el){
38706         return this.addItem(new Roo.menu.BaseItem(el));
38707     },
38708
38709     /**
38710      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38711      * @param {Roo.menu.Item} item The menu item to add
38712      * @return {Roo.menu.Item} The menu item that was added
38713      */
38714     addItem : function(item){
38715         this.items.add(item);
38716         if(this.ul){
38717             var li = document.createElement("li");
38718             li.className = "x-menu-list-item";
38719             this.ul.dom.appendChild(li);
38720             item.render(li, this);
38721             this.delayAutoWidth();
38722         }
38723         return item;
38724     },
38725
38726     /**
38727      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38728      * @param {Object} config A MenuItem config object
38729      * @return {Roo.menu.Item} The menu item that was added
38730      */
38731     addMenuItem : function(config){
38732         if(!(config instanceof Roo.menu.Item)){
38733             if(typeof config.checked == "boolean"){ // must be check menu item config?
38734                 config = new Roo.menu.CheckItem(config);
38735             }else{
38736                 config = new Roo.menu.Item(config);
38737             }
38738         }
38739         return this.addItem(config);
38740     },
38741
38742     /**
38743      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38744      * @param {String} text The text to display in the menu item
38745      * @return {Roo.menu.Item} The menu item that was added
38746      */
38747     addText : function(text){
38748         return this.addItem(new Roo.menu.TextItem({ text : text }));
38749     },
38750
38751     /**
38752      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38753      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38754      * @param {Roo.menu.Item} item The menu item to add
38755      * @return {Roo.menu.Item} The menu item that was added
38756      */
38757     insert : function(index, item){
38758         this.items.insert(index, item);
38759         if(this.ul){
38760             var li = document.createElement("li");
38761             li.className = "x-menu-list-item";
38762             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38763             item.render(li, this);
38764             this.delayAutoWidth();
38765         }
38766         return item;
38767     },
38768
38769     /**
38770      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38771      * @param {Roo.menu.Item} item The menu item to remove
38772      */
38773     remove : function(item){
38774         this.items.removeKey(item.id);
38775         item.destroy();
38776     },
38777
38778     /**
38779      * Removes and destroys all items in the menu
38780      */
38781     removeAll : function(){
38782         var f;
38783         while(f = this.items.first()){
38784             this.remove(f);
38785         }
38786     }
38787 });
38788
38789 // MenuNav is a private utility class used internally by the Menu
38790 Roo.menu.MenuNav = function(menu){
38791     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38792     this.scope = this.menu = menu;
38793 };
38794
38795 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38796     doRelay : function(e, h){
38797         var k = e.getKey();
38798         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38799             this.menu.tryActivate(0, 1);
38800             return false;
38801         }
38802         return h.call(this.scope || this, e, this.menu);
38803     },
38804
38805     up : function(e, m){
38806         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38807             m.tryActivate(m.items.length-1, -1);
38808         }
38809     },
38810
38811     down : function(e, m){
38812         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38813             m.tryActivate(0, 1);
38814         }
38815     },
38816
38817     right : function(e, m){
38818         if(m.activeItem){
38819             m.activeItem.expandMenu(true);
38820         }
38821     },
38822
38823     left : function(e, m){
38824         m.hide();
38825         if(m.parentMenu && m.parentMenu.activeItem){
38826             m.parentMenu.activeItem.activate();
38827         }
38828     },
38829
38830     enter : function(e, m){
38831         if(m.activeItem){
38832             e.stopPropagation();
38833             m.activeItem.onClick(e);
38834             m.fireEvent("click", this, m.activeItem);
38835             return true;
38836         }
38837     }
38838 });/*
38839  * Based on:
38840  * Ext JS Library 1.1.1
38841  * Copyright(c) 2006-2007, Ext JS, LLC.
38842  *
38843  * Originally Released Under LGPL - original licence link has changed is not relivant.
38844  *
38845  * Fork - LGPL
38846  * <script type="text/javascript">
38847  */
38848  
38849 /**
38850  * @class Roo.menu.MenuMgr
38851  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38852  * @static
38853  */
38854 Roo.menu.MenuMgr = function(){
38855    var menus, active, groups = {}, attached = false, lastShow = new Date();
38856
38857    // private - called when first menu is created
38858    function init(){
38859        menus = {};
38860        active = new Roo.util.MixedCollection();
38861        Roo.get(document).addKeyListener(27, function(){
38862            if(active.length > 0){
38863                hideAll();
38864            }
38865        });
38866    }
38867
38868    // private
38869    function hideAll(){
38870        if(active && active.length > 0){
38871            var c = active.clone();
38872            c.each(function(m){
38873                m.hide();
38874            });
38875        }
38876    }
38877
38878    // private
38879    function onHide(m){
38880        active.remove(m);
38881        if(active.length < 1){
38882            Roo.get(document).un("mousedown", onMouseDown);
38883            attached = false;
38884        }
38885    }
38886
38887    // private
38888    function onShow(m){
38889        var last = active.last();
38890        lastShow = new Date();
38891        active.add(m);
38892        if(!attached){
38893            Roo.get(document).on("mousedown", onMouseDown);
38894            attached = true;
38895        }
38896        if(m.parentMenu){
38897           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38898           m.parentMenu.activeChild = m;
38899        }else if(last && last.isVisible()){
38900           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38901        }
38902    }
38903
38904    // private
38905    function onBeforeHide(m){
38906        if(m.activeChild){
38907            m.activeChild.hide();
38908        }
38909        if(m.autoHideTimer){
38910            clearTimeout(m.autoHideTimer);
38911            delete m.autoHideTimer;
38912        }
38913    }
38914
38915    // private
38916    function onBeforeShow(m){
38917        var pm = m.parentMenu;
38918        if(!pm && !m.allowOtherMenus){
38919            hideAll();
38920        }else if(pm && pm.activeChild && active != m){
38921            pm.activeChild.hide();
38922        }
38923    }
38924
38925    // private
38926    function onMouseDown(e){
38927        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38928            hideAll();
38929        }
38930    }
38931
38932    // private
38933    function onBeforeCheck(mi, state){
38934        if(state){
38935            var g = groups[mi.group];
38936            for(var i = 0, l = g.length; i < l; i++){
38937                if(g[i] != mi){
38938                    g[i].setChecked(false);
38939                }
38940            }
38941        }
38942    }
38943
38944    return {
38945
38946        /**
38947         * Hides all menus that are currently visible
38948         */
38949        hideAll : function(){
38950             hideAll();  
38951        },
38952
38953        // private
38954        register : function(menu){
38955            if(!menus){
38956                init();
38957            }
38958            menus[menu.id] = menu;
38959            menu.on("beforehide", onBeforeHide);
38960            menu.on("hide", onHide);
38961            menu.on("beforeshow", onBeforeShow);
38962            menu.on("show", onShow);
38963            var g = menu.group;
38964            if(g && menu.events["checkchange"]){
38965                if(!groups[g]){
38966                    groups[g] = [];
38967                }
38968                groups[g].push(menu);
38969                menu.on("checkchange", onCheck);
38970            }
38971        },
38972
38973         /**
38974          * Returns a {@link Roo.menu.Menu} object
38975          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38976          * be used to generate and return a new Menu instance.
38977          */
38978        get : function(menu){
38979            if(typeof menu == "string"){ // menu id
38980                return menus[menu];
38981            }else if(menu.events){  // menu instance
38982                return menu;
38983            }else if(typeof menu.length == 'number'){ // array of menu items?
38984                return new Roo.menu.Menu({items:menu});
38985            }else{ // otherwise, must be a config
38986                return new Roo.menu.Menu(menu);
38987            }
38988        },
38989
38990        // private
38991        unregister : function(menu){
38992            delete menus[menu.id];
38993            menu.un("beforehide", onBeforeHide);
38994            menu.un("hide", onHide);
38995            menu.un("beforeshow", onBeforeShow);
38996            menu.un("show", onShow);
38997            var g = menu.group;
38998            if(g && menu.events["checkchange"]){
38999                groups[g].remove(menu);
39000                menu.un("checkchange", onCheck);
39001            }
39002        },
39003
39004        // private
39005        registerCheckable : function(menuItem){
39006            var g = menuItem.group;
39007            if(g){
39008                if(!groups[g]){
39009                    groups[g] = [];
39010                }
39011                groups[g].push(menuItem);
39012                menuItem.on("beforecheckchange", onBeforeCheck);
39013            }
39014        },
39015
39016        // private
39017        unregisterCheckable : function(menuItem){
39018            var g = menuItem.group;
39019            if(g){
39020                groups[g].remove(menuItem);
39021                menuItem.un("beforecheckchange", onBeforeCheck);
39022            }
39023        }
39024    };
39025 }();/*
39026  * Based on:
39027  * Ext JS Library 1.1.1
39028  * Copyright(c) 2006-2007, Ext JS, LLC.
39029  *
39030  * Originally Released Under LGPL - original licence link has changed is not relivant.
39031  *
39032  * Fork - LGPL
39033  * <script type="text/javascript">
39034  */
39035  
39036
39037 /**
39038  * @class Roo.menu.BaseItem
39039  * @extends Roo.Component
39040  * @abstract
39041  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39042  * management and base configuration options shared by all menu components.
39043  * @constructor
39044  * Creates a new BaseItem
39045  * @param {Object} config Configuration options
39046  */
39047 Roo.menu.BaseItem = function(config){
39048     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39049
39050     this.addEvents({
39051         /**
39052          * @event click
39053          * Fires when this item is clicked
39054          * @param {Roo.menu.BaseItem} this
39055          * @param {Roo.EventObject} e
39056          */
39057         click: true,
39058         /**
39059          * @event activate
39060          * Fires when this item is activated
39061          * @param {Roo.menu.BaseItem} this
39062          */
39063         activate : true,
39064         /**
39065          * @event deactivate
39066          * Fires when this item is deactivated
39067          * @param {Roo.menu.BaseItem} this
39068          */
39069         deactivate : true
39070     });
39071
39072     if(this.handler){
39073         this.on("click", this.handler, this.scope, true);
39074     }
39075 };
39076
39077 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39078     /**
39079      * @cfg {Function} handler
39080      * A function that will handle the click event of this menu item (defaults to undefined)
39081      */
39082     /**
39083      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39084      */
39085     canActivate : false,
39086     
39087      /**
39088      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39089      */
39090     hidden: false,
39091     
39092     /**
39093      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39094      */
39095     activeClass : "x-menu-item-active",
39096     /**
39097      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39098      */
39099     hideOnClick : true,
39100     /**
39101      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39102      */
39103     hideDelay : 100,
39104
39105     // private
39106     ctype: "Roo.menu.BaseItem",
39107
39108     // private
39109     actionMode : "container",
39110
39111     // private
39112     render : function(container, parentMenu){
39113         this.parentMenu = parentMenu;
39114         Roo.menu.BaseItem.superclass.render.call(this, container);
39115         this.container.menuItemId = this.id;
39116     },
39117
39118     // private
39119     onRender : function(container, position){
39120         this.el = Roo.get(this.el);
39121         container.dom.appendChild(this.el.dom);
39122     },
39123
39124     // private
39125     onClick : function(e){
39126         if(!this.disabled && this.fireEvent("click", this, e) !== false
39127                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39128             this.handleClick(e);
39129         }else{
39130             e.stopEvent();
39131         }
39132     },
39133
39134     // private
39135     activate : function(){
39136         if(this.disabled){
39137             return false;
39138         }
39139         var li = this.container;
39140         li.addClass(this.activeClass);
39141         this.region = li.getRegion().adjust(2, 2, -2, -2);
39142         this.fireEvent("activate", this);
39143         return true;
39144     },
39145
39146     // private
39147     deactivate : function(){
39148         this.container.removeClass(this.activeClass);
39149         this.fireEvent("deactivate", this);
39150     },
39151
39152     // private
39153     shouldDeactivate : function(e){
39154         return !this.region || !this.region.contains(e.getPoint());
39155     },
39156
39157     // private
39158     handleClick : function(e){
39159         if(this.hideOnClick){
39160             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39161         }
39162     },
39163
39164     // private
39165     expandMenu : function(autoActivate){
39166         // do nothing
39167     },
39168
39169     // private
39170     hideMenu : function(){
39171         // do nothing
39172     }
39173 });/*
39174  * Based on:
39175  * Ext JS Library 1.1.1
39176  * Copyright(c) 2006-2007, Ext JS, LLC.
39177  *
39178  * Originally Released Under LGPL - original licence link has changed is not relivant.
39179  *
39180  * Fork - LGPL
39181  * <script type="text/javascript">
39182  */
39183  
39184 /**
39185  * @class Roo.menu.Adapter
39186  * @extends Roo.menu.BaseItem
39187  * @abstract
39188  * 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.
39189  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39190  * @constructor
39191  * Creates a new Adapter
39192  * @param {Object} config Configuration options
39193  */
39194 Roo.menu.Adapter = function(component, config){
39195     Roo.menu.Adapter.superclass.constructor.call(this, config);
39196     this.component = component;
39197 };
39198 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39199     // private
39200     canActivate : true,
39201
39202     // private
39203     onRender : function(container, position){
39204         this.component.render(container);
39205         this.el = this.component.getEl();
39206     },
39207
39208     // private
39209     activate : function(){
39210         if(this.disabled){
39211             return false;
39212         }
39213         this.component.focus();
39214         this.fireEvent("activate", this);
39215         return true;
39216     },
39217
39218     // private
39219     deactivate : function(){
39220         this.fireEvent("deactivate", this);
39221     },
39222
39223     // private
39224     disable : function(){
39225         this.component.disable();
39226         Roo.menu.Adapter.superclass.disable.call(this);
39227     },
39228
39229     // private
39230     enable : function(){
39231         this.component.enable();
39232         Roo.menu.Adapter.superclass.enable.call(this);
39233     }
39234 });/*
39235  * Based on:
39236  * Ext JS Library 1.1.1
39237  * Copyright(c) 2006-2007, Ext JS, LLC.
39238  *
39239  * Originally Released Under LGPL - original licence link has changed is not relivant.
39240  *
39241  * Fork - LGPL
39242  * <script type="text/javascript">
39243  */
39244
39245 /**
39246  * @class Roo.menu.TextItem
39247  * @extends Roo.menu.BaseItem
39248  * Adds a static text string to a menu, usually used as either a heading or group separator.
39249  * Note: old style constructor with text is still supported.
39250  * 
39251  * @constructor
39252  * Creates a new TextItem
39253  * @param {Object} cfg Configuration
39254  */
39255 Roo.menu.TextItem = function(cfg){
39256     if (typeof(cfg) == 'string') {
39257         this.text = cfg;
39258     } else {
39259         Roo.apply(this,cfg);
39260     }
39261     
39262     Roo.menu.TextItem.superclass.constructor.call(this);
39263 };
39264
39265 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39266     /**
39267      * @cfg {String} text Text to show on item.
39268      */
39269     text : '',
39270     
39271     /**
39272      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39273      */
39274     hideOnClick : false,
39275     /**
39276      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39277      */
39278     itemCls : "x-menu-text",
39279
39280     // private
39281     onRender : function(){
39282         var s = document.createElement("span");
39283         s.className = this.itemCls;
39284         s.innerHTML = this.text;
39285         this.el = s;
39286         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39287     }
39288 });/*
39289  * Based on:
39290  * Ext JS Library 1.1.1
39291  * Copyright(c) 2006-2007, Ext JS, LLC.
39292  *
39293  * Originally Released Under LGPL - original licence link has changed is not relivant.
39294  *
39295  * Fork - LGPL
39296  * <script type="text/javascript">
39297  */
39298
39299 /**
39300  * @class Roo.menu.Separator
39301  * @extends Roo.menu.BaseItem
39302  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39303  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39304  * @constructor
39305  * @param {Object} config Configuration options
39306  */
39307 Roo.menu.Separator = function(config){
39308     Roo.menu.Separator.superclass.constructor.call(this, config);
39309 };
39310
39311 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39312     /**
39313      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39314      */
39315     itemCls : "x-menu-sep",
39316     /**
39317      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39318      */
39319     hideOnClick : false,
39320
39321     // private
39322     onRender : function(li){
39323         var s = document.createElement("span");
39324         s.className = this.itemCls;
39325         s.innerHTML = "&#160;";
39326         this.el = s;
39327         li.addClass("x-menu-sep-li");
39328         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39329     }
39330 });/*
39331  * Based on:
39332  * Ext JS Library 1.1.1
39333  * Copyright(c) 2006-2007, Ext JS, LLC.
39334  *
39335  * Originally Released Under LGPL - original licence link has changed is not relivant.
39336  *
39337  * Fork - LGPL
39338  * <script type="text/javascript">
39339  */
39340 /**
39341  * @class Roo.menu.Item
39342  * @extends Roo.menu.BaseItem
39343  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39344  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39345  * activation and click handling.
39346  * @constructor
39347  * Creates a new Item
39348  * @param {Object} config Configuration options
39349  */
39350 Roo.menu.Item = function(config){
39351     Roo.menu.Item.superclass.constructor.call(this, config);
39352     if(this.menu){
39353         this.menu = Roo.menu.MenuMgr.get(this.menu);
39354     }
39355 };
39356 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39357     /**
39358      * @cfg {Roo.menu.Menu} menu
39359      * A Sub menu
39360      */
39361     /**
39362      * @cfg {String} text
39363      * The text to show on the menu item.
39364      */
39365     text: '',
39366      /**
39367      * @cfg {String} HTML to render in menu
39368      * The text to show on the menu item (HTML version).
39369      */
39370     html: '',
39371     /**
39372      * @cfg {String} icon
39373      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39374      */
39375     icon: undefined,
39376     /**
39377      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39378      */
39379     itemCls : "x-menu-item",
39380     /**
39381      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39382      */
39383     canActivate : true,
39384     /**
39385      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39386      */
39387     showDelay: 200,
39388     // doc'd in BaseItem
39389     hideDelay: 200,
39390
39391     // private
39392     ctype: "Roo.menu.Item",
39393     
39394     // private
39395     onRender : function(container, position){
39396         var el = document.createElement("a");
39397         el.hideFocus = true;
39398         el.unselectable = "on";
39399         el.href = this.href || "#";
39400         if(this.hrefTarget){
39401             el.target = this.hrefTarget;
39402         }
39403         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39404         
39405         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39406         
39407         el.innerHTML = String.format(
39408                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39409                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39410         this.el = el;
39411         Roo.menu.Item.superclass.onRender.call(this, container, position);
39412     },
39413
39414     /**
39415      * Sets the text to display in this menu item
39416      * @param {String} text The text to display
39417      * @param {Boolean} isHTML true to indicate text is pure html.
39418      */
39419     setText : function(text, isHTML){
39420         if (isHTML) {
39421             this.html = text;
39422         } else {
39423             this.text = text;
39424             this.html = '';
39425         }
39426         if(this.rendered){
39427             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39428      
39429             this.el.update(String.format(
39430                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39431                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39432             this.parentMenu.autoWidth();
39433         }
39434     },
39435
39436     // private
39437     handleClick : function(e){
39438         if(!this.href){ // if no link defined, stop the event automatically
39439             e.stopEvent();
39440         }
39441         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39442     },
39443
39444     // private
39445     activate : function(autoExpand){
39446         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39447             this.focus();
39448             if(autoExpand){
39449                 this.expandMenu();
39450             }
39451         }
39452         return true;
39453     },
39454
39455     // private
39456     shouldDeactivate : function(e){
39457         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39458             if(this.menu && this.menu.isVisible()){
39459                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39460             }
39461             return true;
39462         }
39463         return false;
39464     },
39465
39466     // private
39467     deactivate : function(){
39468         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39469         this.hideMenu();
39470     },
39471
39472     // private
39473     expandMenu : function(autoActivate){
39474         if(!this.disabled && this.menu){
39475             clearTimeout(this.hideTimer);
39476             delete this.hideTimer;
39477             if(!this.menu.isVisible() && !this.showTimer){
39478                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39479             }else if (this.menu.isVisible() && autoActivate){
39480                 this.menu.tryActivate(0, 1);
39481             }
39482         }
39483     },
39484
39485     // private
39486     deferExpand : function(autoActivate){
39487         delete this.showTimer;
39488         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39489         if(autoActivate){
39490             this.menu.tryActivate(0, 1);
39491         }
39492     },
39493
39494     // private
39495     hideMenu : function(){
39496         clearTimeout(this.showTimer);
39497         delete this.showTimer;
39498         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39499             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39500         }
39501     },
39502
39503     // private
39504     deferHide : function(){
39505         delete this.hideTimer;
39506         this.menu.hide();
39507     }
39508 });/*
39509  * Based on:
39510  * Ext JS Library 1.1.1
39511  * Copyright(c) 2006-2007, Ext JS, LLC.
39512  *
39513  * Originally Released Under LGPL - original licence link has changed is not relivant.
39514  *
39515  * Fork - LGPL
39516  * <script type="text/javascript">
39517  */
39518  
39519 /**
39520  * @class Roo.menu.CheckItem
39521  * @extends Roo.menu.Item
39522  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39523  * @constructor
39524  * Creates a new CheckItem
39525  * @param {Object} config Configuration options
39526  */
39527 Roo.menu.CheckItem = function(config){
39528     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39529     this.addEvents({
39530         /**
39531          * @event beforecheckchange
39532          * Fires before the checked value is set, providing an opportunity to cancel if needed
39533          * @param {Roo.menu.CheckItem} this
39534          * @param {Boolean} checked The new checked value that will be set
39535          */
39536         "beforecheckchange" : true,
39537         /**
39538          * @event checkchange
39539          * Fires after the checked value has been set
39540          * @param {Roo.menu.CheckItem} this
39541          * @param {Boolean} checked The checked value that was set
39542          */
39543         "checkchange" : true
39544     });
39545     if(this.checkHandler){
39546         this.on('checkchange', this.checkHandler, this.scope);
39547     }
39548 };
39549 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39550     /**
39551      * @cfg {String} group
39552      * All check items with the same group name will automatically be grouped into a single-select
39553      * radio button group (defaults to '')
39554      */
39555     /**
39556      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39557      */
39558     itemCls : "x-menu-item x-menu-check-item",
39559     /**
39560      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39561      */
39562     groupClass : "x-menu-group-item",
39563
39564     /**
39565      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39566      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39567      * initialized with checked = true will be rendered as checked.
39568      */
39569     checked: false,
39570
39571     // private
39572     ctype: "Roo.menu.CheckItem",
39573
39574     // private
39575     onRender : function(c){
39576         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39577         if(this.group){
39578             this.el.addClass(this.groupClass);
39579         }
39580         Roo.menu.MenuMgr.registerCheckable(this);
39581         if(this.checked){
39582             this.checked = false;
39583             this.setChecked(true, true);
39584         }
39585     },
39586
39587     // private
39588     destroy : function(){
39589         if(this.rendered){
39590             Roo.menu.MenuMgr.unregisterCheckable(this);
39591         }
39592         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39593     },
39594
39595     /**
39596      * Set the checked state of this item
39597      * @param {Boolean} checked The new checked value
39598      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39599      */
39600     setChecked : function(state, suppressEvent){
39601         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39602             if(this.container){
39603                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39604             }
39605             this.checked = state;
39606             if(suppressEvent !== true){
39607                 this.fireEvent("checkchange", this, state);
39608             }
39609         }
39610     },
39611
39612     // private
39613     handleClick : function(e){
39614        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39615            this.setChecked(!this.checked);
39616        }
39617        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39618     }
39619 });/*
39620  * Based on:
39621  * Ext JS Library 1.1.1
39622  * Copyright(c) 2006-2007, Ext JS, LLC.
39623  *
39624  * Originally Released Under LGPL - original licence link has changed is not relivant.
39625  *
39626  * Fork - LGPL
39627  * <script type="text/javascript">
39628  */
39629  
39630 /**
39631  * @class Roo.menu.DateItem
39632  * @extends Roo.menu.Adapter
39633  * A menu item that wraps the {@link Roo.DatPicker} component.
39634  * @constructor
39635  * Creates a new DateItem
39636  * @param {Object} config Configuration options
39637  */
39638 Roo.menu.DateItem = function(config){
39639     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39640     /** The Roo.DatePicker object @type Roo.DatePicker */
39641     this.picker = this.component;
39642     this.addEvents({select: true});
39643     
39644     this.picker.on("render", function(picker){
39645         picker.getEl().swallowEvent("click");
39646         picker.container.addClass("x-menu-date-item");
39647     });
39648
39649     this.picker.on("select", this.onSelect, this);
39650 };
39651
39652 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39653     // private
39654     onSelect : function(picker, date){
39655         this.fireEvent("select", this, date, picker);
39656         Roo.menu.DateItem.superclass.handleClick.call(this);
39657     }
39658 });/*
39659  * Based on:
39660  * Ext JS Library 1.1.1
39661  * Copyright(c) 2006-2007, Ext JS, LLC.
39662  *
39663  * Originally Released Under LGPL - original licence link has changed is not relivant.
39664  *
39665  * Fork - LGPL
39666  * <script type="text/javascript">
39667  */
39668  
39669 /**
39670  * @class Roo.menu.ColorItem
39671  * @extends Roo.menu.Adapter
39672  * A menu item that wraps the {@link Roo.ColorPalette} component.
39673  * @constructor
39674  * Creates a new ColorItem
39675  * @param {Object} config Configuration options
39676  */
39677 Roo.menu.ColorItem = function(config){
39678     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39679     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39680     this.palette = this.component;
39681     this.relayEvents(this.palette, ["select"]);
39682     if(this.selectHandler){
39683         this.on('select', this.selectHandler, this.scope);
39684     }
39685 };
39686 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39687  * Based on:
39688  * Ext JS Library 1.1.1
39689  * Copyright(c) 2006-2007, Ext JS, LLC.
39690  *
39691  * Originally Released Under LGPL - original licence link has changed is not relivant.
39692  *
39693  * Fork - LGPL
39694  * <script type="text/javascript">
39695  */
39696  
39697
39698 /**
39699  * @class Roo.menu.DateMenu
39700  * @extends Roo.menu.Menu
39701  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39702  * @constructor
39703  * Creates a new DateMenu
39704  * @param {Object} config Configuration options
39705  */
39706 Roo.menu.DateMenu = function(config){
39707     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39708     this.plain = true;
39709     var di = new Roo.menu.DateItem(config);
39710     this.add(di);
39711     /**
39712      * The {@link Roo.DatePicker} instance for this DateMenu
39713      * @type DatePicker
39714      */
39715     this.picker = di.picker;
39716     /**
39717      * @event select
39718      * @param {DatePicker} picker
39719      * @param {Date} date
39720      */
39721     this.relayEvents(di, ["select"]);
39722     this.on('beforeshow', function(){
39723         if(this.picker){
39724             this.picker.hideMonthPicker(false);
39725         }
39726     }, this);
39727 };
39728 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39729     cls:'x-date-menu'
39730 });/*
39731  * Based on:
39732  * Ext JS Library 1.1.1
39733  * Copyright(c) 2006-2007, Ext JS, LLC.
39734  *
39735  * Originally Released Under LGPL - original licence link has changed is not relivant.
39736  *
39737  * Fork - LGPL
39738  * <script type="text/javascript">
39739  */
39740  
39741
39742 /**
39743  * @class Roo.menu.ColorMenu
39744  * @extends Roo.menu.Menu
39745  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39746  * @constructor
39747  * Creates a new ColorMenu
39748  * @param {Object} config Configuration options
39749  */
39750 Roo.menu.ColorMenu = function(config){
39751     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39752     this.plain = true;
39753     var ci = new Roo.menu.ColorItem(config);
39754     this.add(ci);
39755     /**
39756      * The {@link Roo.ColorPalette} instance for this ColorMenu
39757      * @type ColorPalette
39758      */
39759     this.palette = ci.palette;
39760     /**
39761      * @event select
39762      * @param {ColorPalette} palette
39763      * @param {String} color
39764      */
39765     this.relayEvents(ci, ["select"]);
39766 };
39767 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39768  * Based on:
39769  * Ext JS Library 1.1.1
39770  * Copyright(c) 2006-2007, Ext JS, LLC.
39771  *
39772  * Originally Released Under LGPL - original licence link has changed is not relivant.
39773  *
39774  * Fork - LGPL
39775  * <script type="text/javascript">
39776  */
39777  
39778 /**
39779  * @class Roo.form.TextItem
39780  * @extends Roo.BoxComponent
39781  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39782  * @constructor
39783  * Creates a new TextItem
39784  * @param {Object} config Configuration options
39785  */
39786 Roo.form.TextItem = function(config){
39787     Roo.form.TextItem.superclass.constructor.call(this, config);
39788 };
39789
39790 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39791     
39792     /**
39793      * @cfg {String} tag the tag for this item (default div)
39794      */
39795     tag : 'div',
39796     /**
39797      * @cfg {String} html the content for this item
39798      */
39799     html : '',
39800     
39801     getAutoCreate : function()
39802     {
39803         var cfg = {
39804             id: this.id,
39805             tag: this.tag,
39806             html: this.html,
39807             cls: 'x-form-item'
39808         };
39809         
39810         return cfg;
39811         
39812     },
39813     
39814     onRender : function(ct, position)
39815     {
39816         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39817         
39818         if(!this.el){
39819             var cfg = this.getAutoCreate();
39820             if(!cfg.name){
39821                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39822             }
39823             if (!cfg.name.length) {
39824                 delete cfg.name;
39825             }
39826             this.el = ct.createChild(cfg, position);
39827         }
39828     },
39829     /*
39830      * setHTML
39831      * @param {String} html update the Contents of the element.
39832      */
39833     setHTML : function(html)
39834     {
39835         this.fieldEl.dom.innerHTML = html;
39836     }
39837     
39838 });/*
39839  * Based on:
39840  * Ext JS Library 1.1.1
39841  * Copyright(c) 2006-2007, Ext JS, LLC.
39842  *
39843  * Originally Released Under LGPL - original licence link has changed is not relivant.
39844  *
39845  * Fork - LGPL
39846  * <script type="text/javascript">
39847  */
39848  
39849 /**
39850  * @class Roo.form.Field
39851  * @extends Roo.BoxComponent
39852  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39853  * @constructor
39854  * Creates a new Field
39855  * @param {Object} config Configuration options
39856  */
39857 Roo.form.Field = function(config){
39858     Roo.form.Field.superclass.constructor.call(this, config);
39859 };
39860
39861 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39862     /**
39863      * @cfg {String} fieldLabel Label to use when rendering a form.
39864      */
39865        /**
39866      * @cfg {String} qtip Mouse over tip
39867      */
39868      
39869     /**
39870      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39871      */
39872     invalidClass : "x-form-invalid",
39873     /**
39874      * @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")
39875      */
39876     invalidText : "The value in this field is invalid",
39877     /**
39878      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39879      */
39880     focusClass : "x-form-focus",
39881     /**
39882      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39883       automatic validation (defaults to "keyup").
39884      */
39885     validationEvent : "keyup",
39886     /**
39887      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39888      */
39889     validateOnBlur : true,
39890     /**
39891      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39892      */
39893     validationDelay : 250,
39894     /**
39895      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39896      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39897      */
39898     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39899     /**
39900      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39901      */
39902     fieldClass : "x-form-field",
39903     /**
39904      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39905      *<pre>
39906 Value         Description
39907 -----------   ----------------------------------------------------------------------
39908 qtip          Display a quick tip when the user hovers over the field
39909 title         Display a default browser title attribute popup
39910 under         Add a block div beneath the field containing the error text
39911 side          Add an error icon to the right of the field with a popup on hover
39912 [element id]  Add the error text directly to the innerHTML of the specified element
39913 </pre>
39914      */
39915     msgTarget : 'qtip',
39916     /**
39917      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39918      */
39919     msgFx : 'normal',
39920
39921     /**
39922      * @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.
39923      */
39924     readOnly : false,
39925
39926     /**
39927      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39928      */
39929     disabled : false,
39930
39931     /**
39932      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39933      */
39934     inputType : undefined,
39935     
39936     /**
39937      * @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).
39938          */
39939         tabIndex : undefined,
39940         
39941     // private
39942     isFormField : true,
39943
39944     // private
39945     hasFocus : false,
39946     /**
39947      * @property {Roo.Element} fieldEl
39948      * Element Containing the rendered Field (with label etc.)
39949      */
39950     /**
39951      * @cfg {Mixed} value A value to initialize this field with.
39952      */
39953     value : undefined,
39954
39955     /**
39956      * @cfg {String} name The field's HTML name attribute.
39957      */
39958     /**
39959      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39960      */
39961     // private
39962     loadedValue : false,
39963      
39964      
39965         // private ??
39966         initComponent : function(){
39967         Roo.form.Field.superclass.initComponent.call(this);
39968         this.addEvents({
39969             /**
39970              * @event focus
39971              * Fires when this field receives input focus.
39972              * @param {Roo.form.Field} this
39973              */
39974             focus : true,
39975             /**
39976              * @event blur
39977              * Fires when this field loses input focus.
39978              * @param {Roo.form.Field} this
39979              */
39980             blur : true,
39981             /**
39982              * @event specialkey
39983              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39984              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39985              * @param {Roo.form.Field} this
39986              * @param {Roo.EventObject} e The event object
39987              */
39988             specialkey : true,
39989             /**
39990              * @event change
39991              * Fires just before the field blurs if the field value has changed.
39992              * @param {Roo.form.Field} this
39993              * @param {Mixed} newValue The new value
39994              * @param {Mixed} oldValue The original value
39995              */
39996             change : true,
39997             /**
39998              * @event invalid
39999              * Fires after the field has been marked as invalid.
40000              * @param {Roo.form.Field} this
40001              * @param {String} msg The validation message
40002              */
40003             invalid : true,
40004             /**
40005              * @event valid
40006              * Fires after the field has been validated with no errors.
40007              * @param {Roo.form.Field} this
40008              */
40009             valid : true,
40010              /**
40011              * @event keyup
40012              * Fires after the key up
40013              * @param {Roo.form.Field} this
40014              * @param {Roo.EventObject}  e The event Object
40015              */
40016             keyup : true
40017         });
40018     },
40019
40020     /**
40021      * Returns the name attribute of the field if available
40022      * @return {String} name The field name
40023      */
40024     getName: function(){
40025          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40026     },
40027
40028     // private
40029     onRender : function(ct, position){
40030         Roo.form.Field.superclass.onRender.call(this, ct, position);
40031         if(!this.el){
40032             var cfg = this.getAutoCreate();
40033             if(!cfg.name){
40034                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40035             }
40036             if (!cfg.name.length) {
40037                 delete cfg.name;
40038             }
40039             if(this.inputType){
40040                 cfg.type = this.inputType;
40041             }
40042             this.el = ct.createChild(cfg, position);
40043         }
40044         var type = this.el.dom.type;
40045         if(type){
40046             if(type == 'password'){
40047                 type = 'text';
40048             }
40049             this.el.addClass('x-form-'+type);
40050         }
40051         if(this.readOnly){
40052             this.el.dom.readOnly = true;
40053         }
40054         if(this.tabIndex !== undefined){
40055             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40056         }
40057
40058         this.el.addClass([this.fieldClass, this.cls]);
40059         this.initValue();
40060     },
40061
40062     /**
40063      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40064      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40065      * @return {Roo.form.Field} this
40066      */
40067     applyTo : function(target){
40068         this.allowDomMove = false;
40069         this.el = Roo.get(target);
40070         this.render(this.el.dom.parentNode);
40071         return this;
40072     },
40073
40074     // private
40075     initValue : function(){
40076         if(this.value !== undefined){
40077             this.setValue(this.value);
40078         }else if(this.el.dom.value.length > 0){
40079             this.setValue(this.el.dom.value);
40080         }
40081     },
40082
40083     /**
40084      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40085      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40086      */
40087     isDirty : function() {
40088         if(this.disabled) {
40089             return false;
40090         }
40091         return String(this.getValue()) !== String(this.originalValue);
40092     },
40093
40094     /**
40095      * stores the current value in loadedValue
40096      */
40097     resetHasChanged : function()
40098     {
40099         this.loadedValue = String(this.getValue());
40100     },
40101     /**
40102      * checks the current value against the 'loaded' value.
40103      * Note - will return false if 'resetHasChanged' has not been called first.
40104      */
40105     hasChanged : function()
40106     {
40107         if(this.disabled || this.readOnly) {
40108             return false;
40109         }
40110         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40111     },
40112     
40113     
40114     
40115     // private
40116     afterRender : function(){
40117         Roo.form.Field.superclass.afterRender.call(this);
40118         this.initEvents();
40119     },
40120
40121     // private
40122     fireKey : function(e){
40123         //Roo.log('field ' + e.getKey());
40124         if(e.isNavKeyPress()){
40125             this.fireEvent("specialkey", this, e);
40126         }
40127     },
40128
40129     /**
40130      * Resets the current field value to the originally loaded value and clears any validation messages
40131      */
40132     reset : function(){
40133         this.setValue(this.resetValue);
40134         this.originalValue = this.getValue();
40135         this.clearInvalid();
40136     },
40137
40138     // private
40139     initEvents : function(){
40140         // safari killled keypress - so keydown is now used..
40141         this.el.on("keydown" , this.fireKey,  this);
40142         this.el.on("focus", this.onFocus,  this);
40143         this.el.on("blur", this.onBlur,  this);
40144         this.el.relayEvent('keyup', this);
40145
40146         // reference to original value for reset
40147         this.originalValue = this.getValue();
40148         this.resetValue =  this.getValue();
40149     },
40150
40151     // private
40152     onFocus : function(){
40153         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40154             this.el.addClass(this.focusClass);
40155         }
40156         if(!this.hasFocus){
40157             this.hasFocus = true;
40158             this.startValue = this.getValue();
40159             this.fireEvent("focus", this);
40160         }
40161     },
40162
40163     beforeBlur : Roo.emptyFn,
40164
40165     // private
40166     onBlur : function(){
40167         this.beforeBlur();
40168         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40169             this.el.removeClass(this.focusClass);
40170         }
40171         this.hasFocus = false;
40172         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40173             this.validate();
40174         }
40175         var v = this.getValue();
40176         if(String(v) !== String(this.startValue)){
40177             this.fireEvent('change', this, v, this.startValue);
40178         }
40179         this.fireEvent("blur", this);
40180     },
40181
40182     /**
40183      * Returns whether or not the field value is currently valid
40184      * @param {Boolean} preventMark True to disable marking the field invalid
40185      * @return {Boolean} True if the value is valid, else false
40186      */
40187     isValid : function(preventMark){
40188         if(this.disabled){
40189             return true;
40190         }
40191         var restore = this.preventMark;
40192         this.preventMark = preventMark === true;
40193         var v = this.validateValue(this.processValue(this.getRawValue()));
40194         this.preventMark = restore;
40195         return v;
40196     },
40197
40198     /**
40199      * Validates the field value
40200      * @return {Boolean} True if the value is valid, else false
40201      */
40202     validate : function(){
40203         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40204             this.clearInvalid();
40205             return true;
40206         }
40207         return false;
40208     },
40209
40210     processValue : function(value){
40211         return value;
40212     },
40213
40214     // private
40215     // Subclasses should provide the validation implementation by overriding this
40216     validateValue : function(value){
40217         return true;
40218     },
40219
40220     /**
40221      * Mark this field as invalid
40222      * @param {String} msg The validation message
40223      */
40224     markInvalid : function(msg){
40225         if(!this.rendered || this.preventMark){ // not rendered
40226             return;
40227         }
40228         
40229         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40230         
40231         obj.el.addClass(this.invalidClass);
40232         msg = msg || this.invalidText;
40233         switch(this.msgTarget){
40234             case 'qtip':
40235                 obj.el.dom.qtip = msg;
40236                 obj.el.dom.qclass = 'x-form-invalid-tip';
40237                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40238                     Roo.QuickTips.enable();
40239                 }
40240                 break;
40241             case 'title':
40242                 this.el.dom.title = msg;
40243                 break;
40244             case 'under':
40245                 if(!this.errorEl){
40246                     var elp = this.el.findParent('.x-form-element', 5, true);
40247                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40248                     this.errorEl.setWidth(elp.getWidth(true)-20);
40249                 }
40250                 this.errorEl.update(msg);
40251                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40252                 break;
40253             case 'side':
40254                 if(!this.errorIcon){
40255                     var elp = this.el.findParent('.x-form-element', 5, true);
40256                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40257                 }
40258                 this.alignErrorIcon();
40259                 this.errorIcon.dom.qtip = msg;
40260                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40261                 this.errorIcon.show();
40262                 this.on('resize', this.alignErrorIcon, this);
40263                 break;
40264             default:
40265                 var t = Roo.getDom(this.msgTarget);
40266                 t.innerHTML = msg;
40267                 t.style.display = this.msgDisplay;
40268                 break;
40269         }
40270         this.fireEvent('invalid', this, msg);
40271     },
40272
40273     // private
40274     alignErrorIcon : function(){
40275         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40276     },
40277
40278     /**
40279      * Clear any invalid styles/messages for this field
40280      */
40281     clearInvalid : function(){
40282         if(!this.rendered || this.preventMark){ // not rendered
40283             return;
40284         }
40285         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40286         
40287         obj.el.removeClass(this.invalidClass);
40288         switch(this.msgTarget){
40289             case 'qtip':
40290                 obj.el.dom.qtip = '';
40291                 break;
40292             case 'title':
40293                 this.el.dom.title = '';
40294                 break;
40295             case 'under':
40296                 if(this.errorEl){
40297                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40298                 }
40299                 break;
40300             case 'side':
40301                 if(this.errorIcon){
40302                     this.errorIcon.dom.qtip = '';
40303                     this.errorIcon.hide();
40304                     this.un('resize', this.alignErrorIcon, this);
40305                 }
40306                 break;
40307             default:
40308                 var t = Roo.getDom(this.msgTarget);
40309                 t.innerHTML = '';
40310                 t.style.display = 'none';
40311                 break;
40312         }
40313         this.fireEvent('valid', this);
40314     },
40315
40316     /**
40317      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40318      * @return {Mixed} value The field value
40319      */
40320     getRawValue : function(){
40321         var v = this.el.getValue();
40322         
40323         return v;
40324     },
40325
40326     /**
40327      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40328      * @return {Mixed} value The field value
40329      */
40330     getValue : function(){
40331         var v = this.el.getValue();
40332          
40333         return v;
40334     },
40335
40336     /**
40337      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40338      * @param {Mixed} value The value to set
40339      */
40340     setRawValue : function(v){
40341         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40342     },
40343
40344     /**
40345      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40346      * @param {Mixed} value The value to set
40347      */
40348     setValue : function(v){
40349         this.value = v;
40350         if(this.rendered){
40351             this.el.dom.value = (v === null || v === undefined ? '' : v);
40352              this.validate();
40353         }
40354     },
40355
40356     adjustSize : function(w, h){
40357         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40358         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40359         return s;
40360     },
40361
40362     adjustWidth : function(tag, w){
40363         tag = tag.toLowerCase();
40364         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40365             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40366                 if(tag == 'input'){
40367                     return w + 2;
40368                 }
40369                 if(tag == 'textarea'){
40370                     return w-2;
40371                 }
40372             }else if(Roo.isOpera){
40373                 if(tag == 'input'){
40374                     return w + 2;
40375                 }
40376                 if(tag == 'textarea'){
40377                     return w-2;
40378                 }
40379             }
40380         }
40381         return w;
40382     }
40383 });
40384
40385
40386 // anything other than normal should be considered experimental
40387 Roo.form.Field.msgFx = {
40388     normal : {
40389         show: function(msgEl, f){
40390             msgEl.setDisplayed('block');
40391         },
40392
40393         hide : function(msgEl, f){
40394             msgEl.setDisplayed(false).update('');
40395         }
40396     },
40397
40398     slide : {
40399         show: function(msgEl, f){
40400             msgEl.slideIn('t', {stopFx:true});
40401         },
40402
40403         hide : function(msgEl, f){
40404             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40405         }
40406     },
40407
40408     slideRight : {
40409         show: function(msgEl, f){
40410             msgEl.fixDisplay();
40411             msgEl.alignTo(f.el, 'tl-tr');
40412             msgEl.slideIn('l', {stopFx:true});
40413         },
40414
40415         hide : function(msgEl, f){
40416             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40417         }
40418     }
40419 };/*
40420  * Based on:
40421  * Ext JS Library 1.1.1
40422  * Copyright(c) 2006-2007, Ext JS, LLC.
40423  *
40424  * Originally Released Under LGPL - original licence link has changed is not relivant.
40425  *
40426  * Fork - LGPL
40427  * <script type="text/javascript">
40428  */
40429  
40430
40431 /**
40432  * @class Roo.form.TextField
40433  * @extends Roo.form.Field
40434  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40435  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40436  * @constructor
40437  * Creates a new TextField
40438  * @param {Object} config Configuration options
40439  */
40440 Roo.form.TextField = function(config){
40441     Roo.form.TextField.superclass.constructor.call(this, config);
40442     this.addEvents({
40443         /**
40444          * @event autosize
40445          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40446          * according to the default logic, but this event provides a hook for the developer to apply additional
40447          * logic at runtime to resize the field if needed.
40448              * @param {Roo.form.Field} this This text field
40449              * @param {Number} width The new field width
40450              */
40451         autosize : true
40452     });
40453 };
40454
40455 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40456     /**
40457      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40458      */
40459     grow : false,
40460     /**
40461      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40462      */
40463     growMin : 30,
40464     /**
40465      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40466      */
40467     growMax : 800,
40468     /**
40469      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40470      */
40471     vtype : null,
40472     /**
40473      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40474      */
40475     maskRe : null,
40476     /**
40477      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40478      */
40479     disableKeyFilter : false,
40480     /**
40481      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40482      */
40483     allowBlank : true,
40484     /**
40485      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40486      */
40487     minLength : 0,
40488     /**
40489      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40490      */
40491     maxLength : Number.MAX_VALUE,
40492     /**
40493      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40494      */
40495     minLengthText : "The minimum length for this field is {0}",
40496     /**
40497      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40498      */
40499     maxLengthText : "The maximum length for this field is {0}",
40500     /**
40501      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40502      */
40503     selectOnFocus : false,
40504     /**
40505      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40506      */    
40507     allowLeadingSpace : false,
40508     /**
40509      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40510      */
40511     blankText : "This field is required",
40512     /**
40513      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40514      * If available, this function will be called only after the basic validators all return true, and will be passed the
40515      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40516      */
40517     validator : null,
40518     /**
40519      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40520      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40521      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40522      */
40523     regex : null,
40524     /**
40525      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40526      */
40527     regexText : "",
40528     /**
40529      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40530      */
40531     emptyText : null,
40532    
40533
40534     // private
40535     initEvents : function()
40536     {
40537         if (this.emptyText) {
40538             this.el.attr('placeholder', this.emptyText);
40539         }
40540         
40541         Roo.form.TextField.superclass.initEvents.call(this);
40542         if(this.validationEvent == 'keyup'){
40543             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40544             this.el.on('keyup', this.filterValidation, this);
40545         }
40546         else if(this.validationEvent !== false){
40547             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40548         }
40549         
40550         if(this.selectOnFocus){
40551             this.on("focus", this.preFocus, this);
40552         }
40553         if (!this.allowLeadingSpace) {
40554             this.on('blur', this.cleanLeadingSpace, this);
40555         }
40556         
40557         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40558             this.el.on("keypress", this.filterKeys, this);
40559         }
40560         if(this.grow){
40561             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40562             this.el.on("click", this.autoSize,  this);
40563         }
40564         if(this.el.is('input[type=password]') && Roo.isSafari){
40565             this.el.on('keydown', this.SafariOnKeyDown, this);
40566         }
40567     },
40568
40569     processValue : function(value){
40570         if(this.stripCharsRe){
40571             var newValue = value.replace(this.stripCharsRe, '');
40572             if(newValue !== value){
40573                 this.setRawValue(newValue);
40574                 return newValue;
40575             }
40576         }
40577         return value;
40578     },
40579
40580     filterValidation : function(e){
40581         if(!e.isNavKeyPress()){
40582             this.validationTask.delay(this.validationDelay);
40583         }
40584     },
40585
40586     // private
40587     onKeyUp : function(e){
40588         if(!e.isNavKeyPress()){
40589             this.autoSize();
40590         }
40591     },
40592     // private - clean the leading white space
40593     cleanLeadingSpace : function(e)
40594     {
40595         if ( this.inputType == 'file') {
40596             return;
40597         }
40598         
40599         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40600     },
40601     /**
40602      * Resets the current field value to the originally-loaded value and clears any validation messages.
40603      *  
40604      */
40605     reset : function(){
40606         Roo.form.TextField.superclass.reset.call(this);
40607        
40608     }, 
40609     // private
40610     preFocus : function(){
40611         
40612         if(this.selectOnFocus){
40613             this.el.dom.select();
40614         }
40615     },
40616
40617     
40618     // private
40619     filterKeys : function(e){
40620         var k = e.getKey();
40621         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40622             return;
40623         }
40624         var c = e.getCharCode(), cc = String.fromCharCode(c);
40625         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40626             return;
40627         }
40628         if(!this.maskRe.test(cc)){
40629             e.stopEvent();
40630         }
40631     },
40632
40633     setValue : function(v){
40634         
40635         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40636         
40637         this.autoSize();
40638     },
40639
40640     /**
40641      * Validates a value according to the field's validation rules and marks the field as invalid
40642      * if the validation fails
40643      * @param {Mixed} value The value to validate
40644      * @return {Boolean} True if the value is valid, else false
40645      */
40646     validateValue : function(value){
40647         if(value.length < 1)  { // if it's blank
40648              if(this.allowBlank){
40649                 this.clearInvalid();
40650                 return true;
40651              }else{
40652                 this.markInvalid(this.blankText);
40653                 return false;
40654              }
40655         }
40656         if(value.length < this.minLength){
40657             this.markInvalid(String.format(this.minLengthText, this.minLength));
40658             return false;
40659         }
40660         if(value.length > this.maxLength){
40661             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40662             return false;
40663         }
40664         if(this.vtype){
40665             var vt = Roo.form.VTypes;
40666             if(!vt[this.vtype](value, this)){
40667                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40668                 return false;
40669             }
40670         }
40671         if(typeof this.validator == "function"){
40672             var msg = this.validator(value);
40673             if(msg !== true){
40674                 this.markInvalid(msg);
40675                 return false;
40676             }
40677         }
40678         if(this.regex && !this.regex.test(value)){
40679             this.markInvalid(this.regexText);
40680             return false;
40681         }
40682         return true;
40683     },
40684
40685     /**
40686      * Selects text in this field
40687      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40688      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40689      */
40690     selectText : function(start, end){
40691         var v = this.getRawValue();
40692         if(v.length > 0){
40693             start = start === undefined ? 0 : start;
40694             end = end === undefined ? v.length : end;
40695             var d = this.el.dom;
40696             if(d.setSelectionRange){
40697                 d.setSelectionRange(start, end);
40698             }else if(d.createTextRange){
40699                 var range = d.createTextRange();
40700                 range.moveStart("character", start);
40701                 range.moveEnd("character", v.length-end);
40702                 range.select();
40703             }
40704         }
40705     },
40706
40707     /**
40708      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40709      * This only takes effect if grow = true, and fires the autosize event.
40710      */
40711     autoSize : function(){
40712         if(!this.grow || !this.rendered){
40713             return;
40714         }
40715         if(!this.metrics){
40716             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40717         }
40718         var el = this.el;
40719         var v = el.dom.value;
40720         var d = document.createElement('div');
40721         d.appendChild(document.createTextNode(v));
40722         v = d.innerHTML;
40723         d = null;
40724         v += "&#160;";
40725         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40726         this.el.setWidth(w);
40727         this.fireEvent("autosize", this, w);
40728     },
40729     
40730     // private
40731     SafariOnKeyDown : function(event)
40732     {
40733         // this is a workaround for a password hang bug on chrome/ webkit.
40734         
40735         var isSelectAll = false;
40736         
40737         if(this.el.dom.selectionEnd > 0){
40738             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40739         }
40740         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40741             event.preventDefault();
40742             this.setValue('');
40743             return;
40744         }
40745         
40746         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40747             
40748             event.preventDefault();
40749             // this is very hacky as keydown always get's upper case.
40750             
40751             var cc = String.fromCharCode(event.getCharCode());
40752             
40753             
40754             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40755             
40756         }
40757         
40758         
40759     }
40760 });/*
40761  * Based on:
40762  * Ext JS Library 1.1.1
40763  * Copyright(c) 2006-2007, Ext JS, LLC.
40764  *
40765  * Originally Released Under LGPL - original licence link has changed is not relivant.
40766  *
40767  * Fork - LGPL
40768  * <script type="text/javascript">
40769  */
40770  
40771 /**
40772  * @class Roo.form.Hidden
40773  * @extends Roo.form.TextField
40774  * Simple Hidden element used on forms 
40775  * 
40776  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40777  * 
40778  * @constructor
40779  * Creates a new Hidden form element.
40780  * @param {Object} config Configuration options
40781  */
40782
40783
40784
40785 // easy hidden field...
40786 Roo.form.Hidden = function(config){
40787     Roo.form.Hidden.superclass.constructor.call(this, config);
40788 };
40789   
40790 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40791     fieldLabel:      '',
40792     inputType:      'hidden',
40793     width:          50,
40794     allowBlank:     true,
40795     labelSeparator: '',
40796     hidden:         true,
40797     itemCls :       'x-form-item-display-none'
40798
40799
40800 });
40801
40802
40803 /*
40804  * Based on:
40805  * Ext JS Library 1.1.1
40806  * Copyright(c) 2006-2007, Ext JS, LLC.
40807  *
40808  * Originally Released Under LGPL - original licence link has changed is not relivant.
40809  *
40810  * Fork - LGPL
40811  * <script type="text/javascript">
40812  */
40813  
40814 /**
40815  * @class Roo.form.TriggerField
40816  * @extends Roo.form.TextField
40817  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40818  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40819  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40820  * for which you can provide a custom implementation.  For example:
40821  * <pre><code>
40822 var trigger = new Roo.form.TriggerField();
40823 trigger.onTriggerClick = myTriggerFn;
40824 trigger.applyTo('my-field');
40825 </code></pre>
40826  *
40827  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40828  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40829  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40830  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40831  * @constructor
40832  * Create a new TriggerField.
40833  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40834  * to the base TextField)
40835  */
40836 Roo.form.TriggerField = function(config){
40837     this.mimicing = false;
40838     Roo.form.TriggerField.superclass.constructor.call(this, config);
40839 };
40840
40841 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40842     /**
40843      * @cfg {String} triggerClass A CSS class to apply to the trigger
40844      */
40845     /**
40846      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40847      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40848      */
40849     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40850     /**
40851      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40852      */
40853     hideTrigger:false,
40854
40855     /** @cfg {Boolean} grow @hide */
40856     /** @cfg {Number} growMin @hide */
40857     /** @cfg {Number} growMax @hide */
40858
40859     /**
40860      * @hide 
40861      * @method
40862      */
40863     autoSize: Roo.emptyFn,
40864     // private
40865     monitorTab : true,
40866     // private
40867     deferHeight : true,
40868
40869     
40870     actionMode : 'wrap',
40871     // private
40872     onResize : function(w, h){
40873         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40874         if(typeof w == 'number'){
40875             var x = w - this.trigger.getWidth();
40876             this.el.setWidth(this.adjustWidth('input', x));
40877             this.trigger.setStyle('left', x+'px');
40878         }
40879     },
40880
40881     // private
40882     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40883
40884     // private
40885     getResizeEl : function(){
40886         return this.wrap;
40887     },
40888
40889     // private
40890     getPositionEl : function(){
40891         return this.wrap;
40892     },
40893
40894     // private
40895     alignErrorIcon : function(){
40896         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40897     },
40898
40899     // private
40900     onRender : function(ct, position){
40901         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40902         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40903         this.trigger = this.wrap.createChild(this.triggerConfig ||
40904                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40905         if(this.hideTrigger){
40906             this.trigger.setDisplayed(false);
40907         }
40908         this.initTrigger();
40909         if(!this.width){
40910             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40911         }
40912     },
40913
40914     // private
40915     initTrigger : function(){
40916         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40917         this.trigger.addClassOnOver('x-form-trigger-over');
40918         this.trigger.addClassOnClick('x-form-trigger-click');
40919     },
40920
40921     // private
40922     onDestroy : function(){
40923         if(this.trigger){
40924             this.trigger.removeAllListeners();
40925             this.trigger.remove();
40926         }
40927         if(this.wrap){
40928             this.wrap.remove();
40929         }
40930         Roo.form.TriggerField.superclass.onDestroy.call(this);
40931     },
40932
40933     // private
40934     onFocus : function(){
40935         Roo.form.TriggerField.superclass.onFocus.call(this);
40936         if(!this.mimicing){
40937             this.wrap.addClass('x-trigger-wrap-focus');
40938             this.mimicing = true;
40939             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40940             if(this.monitorTab){
40941                 this.el.on("keydown", this.checkTab, this);
40942             }
40943         }
40944     },
40945
40946     // private
40947     checkTab : function(e){
40948         if(e.getKey() == e.TAB){
40949             this.triggerBlur();
40950         }
40951     },
40952
40953     // private
40954     onBlur : function(){
40955         // do nothing
40956     },
40957
40958     // private
40959     mimicBlur : function(e, t){
40960         if(!this.wrap.contains(t) && this.validateBlur()){
40961             this.triggerBlur();
40962         }
40963     },
40964
40965     // private
40966     triggerBlur : function(){
40967         this.mimicing = false;
40968         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40969         if(this.monitorTab){
40970             this.el.un("keydown", this.checkTab, this);
40971         }
40972         this.wrap.removeClass('x-trigger-wrap-focus');
40973         Roo.form.TriggerField.superclass.onBlur.call(this);
40974     },
40975
40976     // private
40977     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40978     validateBlur : function(e, t){
40979         return true;
40980     },
40981
40982     // private
40983     onDisable : function(){
40984         Roo.form.TriggerField.superclass.onDisable.call(this);
40985         if(this.wrap){
40986             this.wrap.addClass('x-item-disabled');
40987         }
40988     },
40989
40990     // private
40991     onEnable : function(){
40992         Roo.form.TriggerField.superclass.onEnable.call(this);
40993         if(this.wrap){
40994             this.wrap.removeClass('x-item-disabled');
40995         }
40996     },
40997
40998     // private
40999     onShow : function(){
41000         var ae = this.getActionEl();
41001         
41002         if(ae){
41003             ae.dom.style.display = '';
41004             ae.dom.style.visibility = 'visible';
41005         }
41006     },
41007
41008     // private
41009     
41010     onHide : function(){
41011         var ae = this.getActionEl();
41012         ae.dom.style.display = 'none';
41013     },
41014
41015     /**
41016      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41017      * by an implementing function.
41018      * @method
41019      * @param {EventObject} e
41020      */
41021     onTriggerClick : Roo.emptyFn
41022 });
41023
41024 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41025 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41026 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41027 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41028     initComponent : function(){
41029         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41030
41031         this.triggerConfig = {
41032             tag:'span', cls:'x-form-twin-triggers', cn:[
41033             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41034             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41035         ]};
41036     },
41037
41038     getTrigger : function(index){
41039         return this.triggers[index];
41040     },
41041
41042     initTrigger : function(){
41043         var ts = this.trigger.select('.x-form-trigger', true);
41044         this.wrap.setStyle('overflow', 'hidden');
41045         var triggerField = this;
41046         ts.each(function(t, all, index){
41047             t.hide = function(){
41048                 var w = triggerField.wrap.getWidth();
41049                 this.dom.style.display = 'none';
41050                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41051             };
41052             t.show = function(){
41053                 var w = triggerField.wrap.getWidth();
41054                 this.dom.style.display = '';
41055                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41056             };
41057             var triggerIndex = 'Trigger'+(index+1);
41058
41059             if(this['hide'+triggerIndex]){
41060                 t.dom.style.display = 'none';
41061             }
41062             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41063             t.addClassOnOver('x-form-trigger-over');
41064             t.addClassOnClick('x-form-trigger-click');
41065         }, this);
41066         this.triggers = ts.elements;
41067     },
41068
41069     onTrigger1Click : Roo.emptyFn,
41070     onTrigger2Click : Roo.emptyFn
41071 });/*
41072  * Based on:
41073  * Ext JS Library 1.1.1
41074  * Copyright(c) 2006-2007, Ext JS, LLC.
41075  *
41076  * Originally Released Under LGPL - original licence link has changed is not relivant.
41077  *
41078  * Fork - LGPL
41079  * <script type="text/javascript">
41080  */
41081  
41082 /**
41083  * @class Roo.form.TextArea
41084  * @extends Roo.form.TextField
41085  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41086  * support for auto-sizing.
41087  * @constructor
41088  * Creates a new TextArea
41089  * @param {Object} config Configuration options
41090  */
41091 Roo.form.TextArea = function(config){
41092     Roo.form.TextArea.superclass.constructor.call(this, config);
41093     // these are provided exchanges for backwards compat
41094     // minHeight/maxHeight were replaced by growMin/growMax to be
41095     // compatible with TextField growing config values
41096     if(this.minHeight !== undefined){
41097         this.growMin = this.minHeight;
41098     }
41099     if(this.maxHeight !== undefined){
41100         this.growMax = this.maxHeight;
41101     }
41102 };
41103
41104 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41105     /**
41106      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41107      */
41108     growMin : 60,
41109     /**
41110      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41111      */
41112     growMax: 1000,
41113     /**
41114      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41115      * in the field (equivalent to setting overflow: hidden, defaults to false)
41116      */
41117     preventScrollbars: false,
41118     /**
41119      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41120      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41121      */
41122
41123     // private
41124     onRender : function(ct, position){
41125         if(!this.el){
41126             this.defaultAutoCreate = {
41127                 tag: "textarea",
41128                 style:"width:300px;height:60px;",
41129                 autocomplete: "new-password"
41130             };
41131         }
41132         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41133         if(this.grow){
41134             this.textSizeEl = Roo.DomHelper.append(document.body, {
41135                 tag: "pre", cls: "x-form-grow-sizer"
41136             });
41137             if(this.preventScrollbars){
41138                 this.el.setStyle("overflow", "hidden");
41139             }
41140             this.el.setHeight(this.growMin);
41141         }
41142     },
41143
41144     onDestroy : function(){
41145         if(this.textSizeEl){
41146             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41147         }
41148         Roo.form.TextArea.superclass.onDestroy.call(this);
41149     },
41150
41151     // private
41152     onKeyUp : function(e){
41153         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41154             this.autoSize();
41155         }
41156     },
41157
41158     /**
41159      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41160      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41161      */
41162     autoSize : function(){
41163         if(!this.grow || !this.textSizeEl){
41164             return;
41165         }
41166         var el = this.el;
41167         var v = el.dom.value;
41168         var ts = this.textSizeEl;
41169
41170         ts.innerHTML = '';
41171         ts.appendChild(document.createTextNode(v));
41172         v = ts.innerHTML;
41173
41174         Roo.fly(ts).setWidth(this.el.getWidth());
41175         if(v.length < 1){
41176             v = "&#160;&#160;";
41177         }else{
41178             if(Roo.isIE){
41179                 v = v.replace(/\n/g, '<p>&#160;</p>');
41180             }
41181             v += "&#160;\n&#160;";
41182         }
41183         ts.innerHTML = v;
41184         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41185         if(h != this.lastHeight){
41186             this.lastHeight = h;
41187             this.el.setHeight(h);
41188             this.fireEvent("autosize", this, h);
41189         }
41190     }
41191 });/*
41192  * Based on:
41193  * Ext JS Library 1.1.1
41194  * Copyright(c) 2006-2007, Ext JS, LLC.
41195  *
41196  * Originally Released Under LGPL - original licence link has changed is not relivant.
41197  *
41198  * Fork - LGPL
41199  * <script type="text/javascript">
41200  */
41201  
41202
41203 /**
41204  * @class Roo.form.NumberField
41205  * @extends Roo.form.TextField
41206  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41207  * @constructor
41208  * Creates a new NumberField
41209  * @param {Object} config Configuration options
41210  */
41211 Roo.form.NumberField = function(config){
41212     Roo.form.NumberField.superclass.constructor.call(this, config);
41213 };
41214
41215 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41216     /**
41217      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41218      */
41219     fieldClass: "x-form-field x-form-num-field",
41220     /**
41221      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41222      */
41223     allowDecimals : true,
41224     /**
41225      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41226      */
41227     decimalSeparator : ".",
41228     /**
41229      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41230      */
41231     decimalPrecision : 2,
41232     /**
41233      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41234      */
41235     allowNegative : true,
41236     /**
41237      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41238      */
41239     minValue : Number.NEGATIVE_INFINITY,
41240     /**
41241      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41242      */
41243     maxValue : Number.MAX_VALUE,
41244     /**
41245      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41246      */
41247     minText : "The minimum value for this field is {0}",
41248     /**
41249      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41250      */
41251     maxText : "The maximum value for this field is {0}",
41252     /**
41253      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41254      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41255      */
41256     nanText : "{0} is not a valid number",
41257
41258     // private
41259     initEvents : function(){
41260         Roo.form.NumberField.superclass.initEvents.call(this);
41261         var allowed = "0123456789";
41262         if(this.allowDecimals){
41263             allowed += this.decimalSeparator;
41264         }
41265         if(this.allowNegative){
41266             allowed += "-";
41267         }
41268         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41269         var keyPress = function(e){
41270             var k = e.getKey();
41271             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41272                 return;
41273             }
41274             var c = e.getCharCode();
41275             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41276                 e.stopEvent();
41277             }
41278         };
41279         this.el.on("keypress", keyPress, this);
41280     },
41281
41282     // private
41283     validateValue : function(value){
41284         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41285             return false;
41286         }
41287         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41288              return true;
41289         }
41290         var num = this.parseValue(value);
41291         if(isNaN(num)){
41292             this.markInvalid(String.format(this.nanText, value));
41293             return false;
41294         }
41295         if(num < this.minValue){
41296             this.markInvalid(String.format(this.minText, this.minValue));
41297             return false;
41298         }
41299         if(num > this.maxValue){
41300             this.markInvalid(String.format(this.maxText, this.maxValue));
41301             return false;
41302         }
41303         return true;
41304     },
41305
41306     getValue : function(){
41307         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41308     },
41309
41310     // private
41311     parseValue : function(value){
41312         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41313         return isNaN(value) ? '' : value;
41314     },
41315
41316     // private
41317     fixPrecision : function(value){
41318         var nan = isNaN(value);
41319         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41320             return nan ? '' : value;
41321         }
41322         return parseFloat(value).toFixed(this.decimalPrecision);
41323     },
41324
41325     setValue : function(v){
41326         v = this.fixPrecision(v);
41327         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41328     },
41329
41330     // private
41331     decimalPrecisionFcn : function(v){
41332         return Math.floor(v);
41333     },
41334
41335     beforeBlur : function(){
41336         var v = this.parseValue(this.getRawValue());
41337         if(v){
41338             this.setValue(v);
41339         }
41340     }
41341 });/*
41342  * Based on:
41343  * Ext JS Library 1.1.1
41344  * Copyright(c) 2006-2007, Ext JS, LLC.
41345  *
41346  * Originally Released Under LGPL - original licence link has changed is not relivant.
41347  *
41348  * Fork - LGPL
41349  * <script type="text/javascript">
41350  */
41351  
41352 /**
41353  * @class Roo.form.DateField
41354  * @extends Roo.form.TriggerField
41355  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41356 * @constructor
41357 * Create a new DateField
41358 * @param {Object} config
41359  */
41360 Roo.form.DateField = function(config)
41361 {
41362     Roo.form.DateField.superclass.constructor.call(this, config);
41363     
41364       this.addEvents({
41365          
41366         /**
41367          * @event select
41368          * Fires when a date is selected
41369              * @param {Roo.form.DateField} combo This combo box
41370              * @param {Date} date The date selected
41371              */
41372         'select' : true
41373          
41374     });
41375     
41376     
41377     if(typeof this.minValue == "string") {
41378         this.minValue = this.parseDate(this.minValue);
41379     }
41380     if(typeof this.maxValue == "string") {
41381         this.maxValue = this.parseDate(this.maxValue);
41382     }
41383     this.ddMatch = null;
41384     if(this.disabledDates){
41385         var dd = this.disabledDates;
41386         var re = "(?:";
41387         for(var i = 0; i < dd.length; i++){
41388             re += dd[i];
41389             if(i != dd.length-1) {
41390                 re += "|";
41391             }
41392         }
41393         this.ddMatch = new RegExp(re + ")");
41394     }
41395 };
41396
41397 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41398     /**
41399      * @cfg {String} format
41400      * The default date format string which can be overriden for localization support.  The format must be
41401      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41402      */
41403     format : "m/d/y",
41404     /**
41405      * @cfg {String} altFormats
41406      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41407      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41408      */
41409     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41410     /**
41411      * @cfg {Array} disabledDays
41412      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41413      */
41414     disabledDays : null,
41415     /**
41416      * @cfg {String} disabledDaysText
41417      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41418      */
41419     disabledDaysText : "Disabled",
41420     /**
41421      * @cfg {Array} disabledDates
41422      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41423      * expression so they are very powerful. Some examples:
41424      * <ul>
41425      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41426      * <li>["03/08", "09/16"] would disable those days for every year</li>
41427      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41428      * <li>["03/../2006"] would disable every day in March 2006</li>
41429      * <li>["^03"] would disable every day in every March</li>
41430      * </ul>
41431      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41432      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41433      */
41434     disabledDates : null,
41435     /**
41436      * @cfg {String} disabledDatesText
41437      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41438      */
41439     disabledDatesText : "Disabled",
41440     /**
41441      * @cfg {Date/String} minValue
41442      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41443      * valid format (defaults to null).
41444      */
41445     minValue : null,
41446     /**
41447      * @cfg {Date/String} maxValue
41448      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41449      * valid format (defaults to null).
41450      */
41451     maxValue : null,
41452     /**
41453      * @cfg {String} minText
41454      * The error text to display when the date in the cell is before minValue (defaults to
41455      * 'The date in this field must be after {minValue}').
41456      */
41457     minText : "The date in this field must be equal to or after {0}",
41458     /**
41459      * @cfg {String} maxText
41460      * The error text to display when the date in the cell is after maxValue (defaults to
41461      * 'The date in this field must be before {maxValue}').
41462      */
41463     maxText : "The date in this field must be equal to or before {0}",
41464     /**
41465      * @cfg {String} invalidText
41466      * The error text to display when the date in the field is invalid (defaults to
41467      * '{value} is not a valid date - it must be in the format {format}').
41468      */
41469     invalidText : "{0} is not a valid date - it must be in the format {1}",
41470     /**
41471      * @cfg {String} triggerClass
41472      * An additional CSS class used to style the trigger button.  The trigger will always get the
41473      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41474      * which displays a calendar icon).
41475      */
41476     triggerClass : 'x-form-date-trigger',
41477     
41478
41479     /**
41480      * @cfg {Boolean} useIso
41481      * if enabled, then the date field will use a hidden field to store the 
41482      * real value as iso formated date. default (false)
41483      */ 
41484     useIso : false,
41485     /**
41486      * @cfg {String/Object} autoCreate
41487      * A DomHelper element spec, or true for a default element spec (defaults to
41488      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41489      */ 
41490     // private
41491     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41492     
41493     // private
41494     hiddenField: false,
41495     
41496     onRender : function(ct, position)
41497     {
41498         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41499         if (this.useIso) {
41500             //this.el.dom.removeAttribute('name'); 
41501             Roo.log("Changing name?");
41502             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41503             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41504                     'before', true);
41505             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41506             // prevent input submission
41507             this.hiddenName = this.name;
41508         }
41509             
41510             
41511     },
41512     
41513     // private
41514     validateValue : function(value)
41515     {
41516         value = this.formatDate(value);
41517         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41518             Roo.log('super failed');
41519             return false;
41520         }
41521         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41522              return true;
41523         }
41524         var svalue = value;
41525         value = this.parseDate(value);
41526         if(!value){
41527             Roo.log('parse date failed' + svalue);
41528             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41529             return false;
41530         }
41531         var time = value.getTime();
41532         if(this.minValue && time < this.minValue.getTime()){
41533             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41534             return false;
41535         }
41536         if(this.maxValue && time > this.maxValue.getTime()){
41537             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41538             return false;
41539         }
41540         if(this.disabledDays){
41541             var day = value.getDay();
41542             for(var i = 0; i < this.disabledDays.length; i++) {
41543                 if(day === this.disabledDays[i]){
41544                     this.markInvalid(this.disabledDaysText);
41545                     return false;
41546                 }
41547             }
41548         }
41549         var fvalue = this.formatDate(value);
41550         if(this.ddMatch && this.ddMatch.test(fvalue)){
41551             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41552             return false;
41553         }
41554         return true;
41555     },
41556
41557     // private
41558     // Provides logic to override the default TriggerField.validateBlur which just returns true
41559     validateBlur : function(){
41560         return !this.menu || !this.menu.isVisible();
41561     },
41562     
41563     getName: function()
41564     {
41565         // returns hidden if it's set..
41566         if (!this.rendered) {return ''};
41567         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41568         
41569     },
41570
41571     /**
41572      * Returns the current date value of the date field.
41573      * @return {Date} The date value
41574      */
41575     getValue : function(){
41576         
41577         return  this.hiddenField ?
41578                 this.hiddenField.value :
41579                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41580     },
41581
41582     /**
41583      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41584      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41585      * (the default format used is "m/d/y").
41586      * <br />Usage:
41587      * <pre><code>
41588 //All of these calls set the same date value (May 4, 2006)
41589
41590 //Pass a date object:
41591 var dt = new Date('5/4/06');
41592 dateField.setValue(dt);
41593
41594 //Pass a date string (default format):
41595 dateField.setValue('5/4/06');
41596
41597 //Pass a date string (custom format):
41598 dateField.format = 'Y-m-d';
41599 dateField.setValue('2006-5-4');
41600 </code></pre>
41601      * @param {String/Date} date The date or valid date string
41602      */
41603     setValue : function(date){
41604         if (this.hiddenField) {
41605             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41606         }
41607         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41608         // make sure the value field is always stored as a date..
41609         this.value = this.parseDate(date);
41610         
41611         
41612     },
41613
41614     // private
41615     parseDate : function(value){
41616         if(!value || value instanceof Date){
41617             return value;
41618         }
41619         var v = Date.parseDate(value, this.format);
41620          if (!v && this.useIso) {
41621             v = Date.parseDate(value, 'Y-m-d');
41622         }
41623         if(!v && this.altFormats){
41624             if(!this.altFormatsArray){
41625                 this.altFormatsArray = this.altFormats.split("|");
41626             }
41627             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41628                 v = Date.parseDate(value, this.altFormatsArray[i]);
41629             }
41630         }
41631         return v;
41632     },
41633
41634     // private
41635     formatDate : function(date, fmt){
41636         return (!date || !(date instanceof Date)) ?
41637                date : date.dateFormat(fmt || this.format);
41638     },
41639
41640     // private
41641     menuListeners : {
41642         select: function(m, d){
41643             
41644             this.setValue(d);
41645             this.fireEvent('select', this, d);
41646         },
41647         show : function(){ // retain focus styling
41648             this.onFocus();
41649         },
41650         hide : function(){
41651             this.focus.defer(10, this);
41652             var ml = this.menuListeners;
41653             this.menu.un("select", ml.select,  this);
41654             this.menu.un("show", ml.show,  this);
41655             this.menu.un("hide", ml.hide,  this);
41656         }
41657     },
41658
41659     // private
41660     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41661     onTriggerClick : function(){
41662         if(this.disabled){
41663             return;
41664         }
41665         if(this.menu == null){
41666             this.menu = new Roo.menu.DateMenu();
41667         }
41668         Roo.apply(this.menu.picker,  {
41669             showClear: this.allowBlank,
41670             minDate : this.minValue,
41671             maxDate : this.maxValue,
41672             disabledDatesRE : this.ddMatch,
41673             disabledDatesText : this.disabledDatesText,
41674             disabledDays : this.disabledDays,
41675             disabledDaysText : this.disabledDaysText,
41676             format : this.useIso ? 'Y-m-d' : this.format,
41677             minText : String.format(this.minText, this.formatDate(this.minValue)),
41678             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41679         });
41680         this.menu.on(Roo.apply({}, this.menuListeners, {
41681             scope:this
41682         }));
41683         this.menu.picker.setValue(this.getValue() || new Date());
41684         this.menu.show(this.el, "tl-bl?");
41685     },
41686
41687     beforeBlur : function(){
41688         var v = this.parseDate(this.getRawValue());
41689         if(v){
41690             this.setValue(v);
41691         }
41692     },
41693
41694     /*@
41695      * overide
41696      * 
41697      */
41698     isDirty : function() {
41699         if(this.disabled) {
41700             return false;
41701         }
41702         
41703         if(typeof(this.startValue) === 'undefined'){
41704             return false;
41705         }
41706         
41707         return String(this.getValue()) !== String(this.startValue);
41708         
41709     },
41710     // @overide
41711     cleanLeadingSpace : function(e)
41712     {
41713        return;
41714     }
41715     
41716 });/*
41717  * Based on:
41718  * Ext JS Library 1.1.1
41719  * Copyright(c) 2006-2007, Ext JS, LLC.
41720  *
41721  * Originally Released Under LGPL - original licence link has changed is not relivant.
41722  *
41723  * Fork - LGPL
41724  * <script type="text/javascript">
41725  */
41726  
41727 /**
41728  * @class Roo.form.MonthField
41729  * @extends Roo.form.TriggerField
41730  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41731 * @constructor
41732 * Create a new MonthField
41733 * @param {Object} config
41734  */
41735 Roo.form.MonthField = function(config){
41736     
41737     Roo.form.MonthField.superclass.constructor.call(this, config);
41738     
41739       this.addEvents({
41740          
41741         /**
41742          * @event select
41743          * Fires when a date is selected
41744              * @param {Roo.form.MonthFieeld} combo This combo box
41745              * @param {Date} date The date selected
41746              */
41747         'select' : true
41748          
41749     });
41750     
41751     
41752     if(typeof this.minValue == "string") {
41753         this.minValue = this.parseDate(this.minValue);
41754     }
41755     if(typeof this.maxValue == "string") {
41756         this.maxValue = this.parseDate(this.maxValue);
41757     }
41758     this.ddMatch = null;
41759     if(this.disabledDates){
41760         var dd = this.disabledDates;
41761         var re = "(?:";
41762         for(var i = 0; i < dd.length; i++){
41763             re += dd[i];
41764             if(i != dd.length-1) {
41765                 re += "|";
41766             }
41767         }
41768         this.ddMatch = new RegExp(re + ")");
41769     }
41770 };
41771
41772 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41773     /**
41774      * @cfg {String} format
41775      * The default date format string which can be overriden for localization support.  The format must be
41776      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41777      */
41778     format : "M Y",
41779     /**
41780      * @cfg {String} altFormats
41781      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41782      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41783      */
41784     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41785     /**
41786      * @cfg {Array} disabledDays
41787      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41788      */
41789     disabledDays : [0,1,2,3,4,5,6],
41790     /**
41791      * @cfg {String} disabledDaysText
41792      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41793      */
41794     disabledDaysText : "Disabled",
41795     /**
41796      * @cfg {Array} disabledDates
41797      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41798      * expression so they are very powerful. Some examples:
41799      * <ul>
41800      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41801      * <li>["03/08", "09/16"] would disable those days for every year</li>
41802      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41803      * <li>["03/../2006"] would disable every day in March 2006</li>
41804      * <li>["^03"] would disable every day in every March</li>
41805      * </ul>
41806      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41807      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41808      */
41809     disabledDates : null,
41810     /**
41811      * @cfg {String} disabledDatesText
41812      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41813      */
41814     disabledDatesText : "Disabled",
41815     /**
41816      * @cfg {Date/String} minValue
41817      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41818      * valid format (defaults to null).
41819      */
41820     minValue : null,
41821     /**
41822      * @cfg {Date/String} maxValue
41823      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41824      * valid format (defaults to null).
41825      */
41826     maxValue : null,
41827     /**
41828      * @cfg {String} minText
41829      * The error text to display when the date in the cell is before minValue (defaults to
41830      * 'The date in this field must be after {minValue}').
41831      */
41832     minText : "The date in this field must be equal to or after {0}",
41833     /**
41834      * @cfg {String} maxTextf
41835      * The error text to display when the date in the cell is after maxValue (defaults to
41836      * 'The date in this field must be before {maxValue}').
41837      */
41838     maxText : "The date in this field must be equal to or before {0}",
41839     /**
41840      * @cfg {String} invalidText
41841      * The error text to display when the date in the field is invalid (defaults to
41842      * '{value} is not a valid date - it must be in the format {format}').
41843      */
41844     invalidText : "{0} is not a valid date - it must be in the format {1}",
41845     /**
41846      * @cfg {String} triggerClass
41847      * An additional CSS class used to style the trigger button.  The trigger will always get the
41848      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41849      * which displays a calendar icon).
41850      */
41851     triggerClass : 'x-form-date-trigger',
41852     
41853
41854     /**
41855      * @cfg {Boolean} useIso
41856      * if enabled, then the date field will use a hidden field to store the 
41857      * real value as iso formated date. default (true)
41858      */ 
41859     useIso : true,
41860     /**
41861      * @cfg {String/Object} autoCreate
41862      * A DomHelper element spec, or true for a default element spec (defaults to
41863      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41864      */ 
41865     // private
41866     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41867     
41868     // private
41869     hiddenField: false,
41870     
41871     hideMonthPicker : false,
41872     
41873     onRender : function(ct, position)
41874     {
41875         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41876         if (this.useIso) {
41877             this.el.dom.removeAttribute('name'); 
41878             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41879                     'before', true);
41880             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41881             // prevent input submission
41882             this.hiddenName = this.name;
41883         }
41884             
41885             
41886     },
41887     
41888     // private
41889     validateValue : function(value)
41890     {
41891         value = this.formatDate(value);
41892         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41893             return false;
41894         }
41895         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41896              return true;
41897         }
41898         var svalue = value;
41899         value = this.parseDate(value);
41900         if(!value){
41901             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41902             return false;
41903         }
41904         var time = value.getTime();
41905         if(this.minValue && time < this.minValue.getTime()){
41906             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41907             return false;
41908         }
41909         if(this.maxValue && time > this.maxValue.getTime()){
41910             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41911             return false;
41912         }
41913         /*if(this.disabledDays){
41914             var day = value.getDay();
41915             for(var i = 0; i < this.disabledDays.length; i++) {
41916                 if(day === this.disabledDays[i]){
41917                     this.markInvalid(this.disabledDaysText);
41918                     return false;
41919                 }
41920             }
41921         }
41922         */
41923         var fvalue = this.formatDate(value);
41924         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41925             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41926             return false;
41927         }
41928         */
41929         return true;
41930     },
41931
41932     // private
41933     // Provides logic to override the default TriggerField.validateBlur which just returns true
41934     validateBlur : function(){
41935         return !this.menu || !this.menu.isVisible();
41936     },
41937
41938     /**
41939      * Returns the current date value of the date field.
41940      * @return {Date} The date value
41941      */
41942     getValue : function(){
41943         
41944         
41945         
41946         return  this.hiddenField ?
41947                 this.hiddenField.value :
41948                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41949     },
41950
41951     /**
41952      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41953      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41954      * (the default format used is "m/d/y").
41955      * <br />Usage:
41956      * <pre><code>
41957 //All of these calls set the same date value (May 4, 2006)
41958
41959 //Pass a date object:
41960 var dt = new Date('5/4/06');
41961 monthField.setValue(dt);
41962
41963 //Pass a date string (default format):
41964 monthField.setValue('5/4/06');
41965
41966 //Pass a date string (custom format):
41967 monthField.format = 'Y-m-d';
41968 monthField.setValue('2006-5-4');
41969 </code></pre>
41970      * @param {String/Date} date The date or valid date string
41971      */
41972     setValue : function(date){
41973         Roo.log('month setValue' + date);
41974         // can only be first of month..
41975         
41976         var val = this.parseDate(date);
41977         
41978         if (this.hiddenField) {
41979             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41980         }
41981         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41982         this.value = this.parseDate(date);
41983     },
41984
41985     // private
41986     parseDate : function(value){
41987         if(!value || value instanceof Date){
41988             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41989             return value;
41990         }
41991         var v = Date.parseDate(value, this.format);
41992         if (!v && this.useIso) {
41993             v = Date.parseDate(value, 'Y-m-d');
41994         }
41995         if (v) {
41996             // 
41997             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41998         }
41999         
42000         
42001         if(!v && this.altFormats){
42002             if(!this.altFormatsArray){
42003                 this.altFormatsArray = this.altFormats.split("|");
42004             }
42005             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42006                 v = Date.parseDate(value, this.altFormatsArray[i]);
42007             }
42008         }
42009         return v;
42010     },
42011
42012     // private
42013     formatDate : function(date, fmt){
42014         return (!date || !(date instanceof Date)) ?
42015                date : date.dateFormat(fmt || this.format);
42016     },
42017
42018     // private
42019     menuListeners : {
42020         select: function(m, d){
42021             this.setValue(d);
42022             this.fireEvent('select', this, d);
42023         },
42024         show : function(){ // retain focus styling
42025             this.onFocus();
42026         },
42027         hide : function(){
42028             this.focus.defer(10, this);
42029             var ml = this.menuListeners;
42030             this.menu.un("select", ml.select,  this);
42031             this.menu.un("show", ml.show,  this);
42032             this.menu.un("hide", ml.hide,  this);
42033         }
42034     },
42035     // private
42036     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42037     onTriggerClick : function(){
42038         if(this.disabled){
42039             return;
42040         }
42041         if(this.menu == null){
42042             this.menu = new Roo.menu.DateMenu();
42043            
42044         }
42045         
42046         Roo.apply(this.menu.picker,  {
42047             
42048             showClear: this.allowBlank,
42049             minDate : this.minValue,
42050             maxDate : this.maxValue,
42051             disabledDatesRE : this.ddMatch,
42052             disabledDatesText : this.disabledDatesText,
42053             
42054             format : this.useIso ? 'Y-m-d' : this.format,
42055             minText : String.format(this.minText, this.formatDate(this.minValue)),
42056             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42057             
42058         });
42059          this.menu.on(Roo.apply({}, this.menuListeners, {
42060             scope:this
42061         }));
42062        
42063         
42064         var m = this.menu;
42065         var p = m.picker;
42066         
42067         // hide month picker get's called when we called by 'before hide';
42068         
42069         var ignorehide = true;
42070         p.hideMonthPicker  = function(disableAnim){
42071             if (ignorehide) {
42072                 return;
42073             }
42074              if(this.monthPicker){
42075                 Roo.log("hideMonthPicker called");
42076                 if(disableAnim === true){
42077                     this.monthPicker.hide();
42078                 }else{
42079                     this.monthPicker.slideOut('t', {duration:.2});
42080                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42081                     p.fireEvent("select", this, this.value);
42082                     m.hide();
42083                 }
42084             }
42085         }
42086         
42087         Roo.log('picker set value');
42088         Roo.log(this.getValue());
42089         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42090         m.show(this.el, 'tl-bl?');
42091         ignorehide  = false;
42092         // this will trigger hideMonthPicker..
42093         
42094         
42095         // hidden the day picker
42096         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42097         
42098         
42099         
42100       
42101         
42102         p.showMonthPicker.defer(100, p);
42103     
42104         
42105        
42106     },
42107
42108     beforeBlur : function(){
42109         var v = this.parseDate(this.getRawValue());
42110         if(v){
42111             this.setValue(v);
42112         }
42113     }
42114
42115     /** @cfg {Boolean} grow @hide */
42116     /** @cfg {Number} growMin @hide */
42117     /** @cfg {Number} growMax @hide */
42118     /**
42119      * @hide
42120      * @method autoSize
42121      */
42122 });/*
42123  * Based on:
42124  * Ext JS Library 1.1.1
42125  * Copyright(c) 2006-2007, Ext JS, LLC.
42126  *
42127  * Originally Released Under LGPL - original licence link has changed is not relivant.
42128  *
42129  * Fork - LGPL
42130  * <script type="text/javascript">
42131  */
42132  
42133
42134 /**
42135  * @class Roo.form.ComboBox
42136  * @extends Roo.form.TriggerField
42137  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42138  * @constructor
42139  * Create a new ComboBox.
42140  * @param {Object} config Configuration options
42141  */
42142 Roo.form.ComboBox = function(config){
42143     Roo.form.ComboBox.superclass.constructor.call(this, config);
42144     this.addEvents({
42145         /**
42146          * @event expand
42147          * Fires when the dropdown list is expanded
42148              * @param {Roo.form.ComboBox} combo This combo box
42149              */
42150         'expand' : true,
42151         /**
42152          * @event collapse
42153          * Fires when the dropdown list is collapsed
42154              * @param {Roo.form.ComboBox} combo This combo box
42155              */
42156         'collapse' : true,
42157         /**
42158          * @event beforeselect
42159          * Fires before a list item is selected. Return false to cancel the selection.
42160              * @param {Roo.form.ComboBox} combo This combo box
42161              * @param {Roo.data.Record} record The data record returned from the underlying store
42162              * @param {Number} index The index of the selected item in the dropdown list
42163              */
42164         'beforeselect' : true,
42165         /**
42166          * @event select
42167          * Fires when a list item is selected
42168              * @param {Roo.form.ComboBox} combo This combo box
42169              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42170              * @param {Number} index The index of the selected item in the dropdown list
42171              */
42172         'select' : true,
42173         /**
42174          * @event beforequery
42175          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42176          * The event object passed has these properties:
42177              * @param {Roo.form.ComboBox} combo This combo box
42178              * @param {String} query The query
42179              * @param {Boolean} forceAll true to force "all" query
42180              * @param {Boolean} cancel true to cancel the query
42181              * @param {Object} e The query event object
42182              */
42183         'beforequery': true,
42184          /**
42185          * @event add
42186          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42187              * @param {Roo.form.ComboBox} combo This combo box
42188              */
42189         'add' : true,
42190         /**
42191          * @event edit
42192          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42193              * @param {Roo.form.ComboBox} combo This combo box
42194              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42195              */
42196         'edit' : true
42197         
42198         
42199     });
42200     if(this.transform){
42201         this.allowDomMove = false;
42202         var s = Roo.getDom(this.transform);
42203         if(!this.hiddenName){
42204             this.hiddenName = s.name;
42205         }
42206         if(!this.store){
42207             this.mode = 'local';
42208             var d = [], opts = s.options;
42209             for(var i = 0, len = opts.length;i < len; i++){
42210                 var o = opts[i];
42211                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42212                 if(o.selected) {
42213                     this.value = value;
42214                 }
42215                 d.push([value, o.text]);
42216             }
42217             this.store = new Roo.data.SimpleStore({
42218                 'id': 0,
42219                 fields: ['value', 'text'],
42220                 data : d
42221             });
42222             this.valueField = 'value';
42223             this.displayField = 'text';
42224         }
42225         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42226         if(!this.lazyRender){
42227             this.target = true;
42228             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42229             s.parentNode.removeChild(s); // remove it
42230             this.render(this.el.parentNode);
42231         }else{
42232             s.parentNode.removeChild(s); // remove it
42233         }
42234
42235     }
42236     if (this.store) {
42237         this.store = Roo.factory(this.store, Roo.data);
42238     }
42239     
42240     this.selectedIndex = -1;
42241     if(this.mode == 'local'){
42242         if(config.queryDelay === undefined){
42243             this.queryDelay = 10;
42244         }
42245         if(config.minChars === undefined){
42246             this.minChars = 0;
42247         }
42248     }
42249 };
42250
42251 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42252     /**
42253      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42254      */
42255     /**
42256      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42257      * rendering into an Roo.Editor, defaults to false)
42258      */
42259     /**
42260      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42261      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42262      */
42263     /**
42264      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42265      */
42266     /**
42267      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42268      * the dropdown list (defaults to undefined, with no header element)
42269      */
42270
42271      /**
42272      * @cfg {String/Roo.Template} tpl The template to use to render the output
42273      */
42274      
42275     // private
42276     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42277     /**
42278      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42279      */
42280     listWidth: undefined,
42281     /**
42282      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42283      * mode = 'remote' or 'text' if mode = 'local')
42284      */
42285     displayField: undefined,
42286     /**
42287      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42288      * mode = 'remote' or 'value' if mode = 'local'). 
42289      * Note: use of a valueField requires the user make a selection
42290      * in order for a value to be mapped.
42291      */
42292     valueField: undefined,
42293     
42294     
42295     /**
42296      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42297      * field's data value (defaults to the underlying DOM element's name)
42298      */
42299     hiddenName: undefined,
42300     /**
42301      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42302      */
42303     listClass: '',
42304     /**
42305      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42306      */
42307     selectedClass: 'x-combo-selected',
42308     /**
42309      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42310      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42311      * which displays a downward arrow icon).
42312      */
42313     triggerClass : 'x-form-arrow-trigger',
42314     /**
42315      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42316      */
42317     shadow:'sides',
42318     /**
42319      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42320      * anchor positions (defaults to 'tl-bl')
42321      */
42322     listAlign: 'tl-bl?',
42323     /**
42324      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42325      */
42326     maxHeight: 300,
42327     /**
42328      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42329      * query specified by the allQuery config option (defaults to 'query')
42330      */
42331     triggerAction: 'query',
42332     /**
42333      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42334      * (defaults to 4, does not apply if editable = false)
42335      */
42336     minChars : 4,
42337     /**
42338      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42339      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42340      */
42341     typeAhead: false,
42342     /**
42343      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42344      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42345      */
42346     queryDelay: 500,
42347     /**
42348      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42349      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42350      */
42351     pageSize: 0,
42352     /**
42353      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42354      * when editable = true (defaults to false)
42355      */
42356     selectOnFocus:false,
42357     /**
42358      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42359      */
42360     queryParam: 'query',
42361     /**
42362      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42363      * when mode = 'remote' (defaults to 'Loading...')
42364      */
42365     loadingText: 'Loading...',
42366     /**
42367      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42368      */
42369     resizable: false,
42370     /**
42371      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42372      */
42373     handleHeight : 8,
42374     /**
42375      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42376      * traditional select (defaults to true)
42377      */
42378     editable: true,
42379     /**
42380      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42381      */
42382     allQuery: '',
42383     /**
42384      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42385      */
42386     mode: 'remote',
42387     /**
42388      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42389      * listWidth has a higher value)
42390      */
42391     minListWidth : 70,
42392     /**
42393      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42394      * allow the user to set arbitrary text into the field (defaults to false)
42395      */
42396     forceSelection:false,
42397     /**
42398      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42399      * if typeAhead = true (defaults to 250)
42400      */
42401     typeAheadDelay : 250,
42402     /**
42403      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42404      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42405      */
42406     valueNotFoundText : undefined,
42407     /**
42408      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42409      */
42410     blockFocus : false,
42411     
42412     /**
42413      * @cfg {Boolean} disableClear Disable showing of clear button.
42414      */
42415     disableClear : false,
42416     /**
42417      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42418      */
42419     alwaysQuery : false,
42420     
42421     //private
42422     addicon : false,
42423     editicon: false,
42424     
42425     // element that contains real text value.. (when hidden is used..)
42426      
42427     // private
42428     onRender : function(ct, position)
42429     {
42430         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42431         
42432         if(this.hiddenName){
42433             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42434                     'before', true);
42435             this.hiddenField.value =
42436                 this.hiddenValue !== undefined ? this.hiddenValue :
42437                 this.value !== undefined ? this.value : '';
42438
42439             // prevent input submission
42440             this.el.dom.removeAttribute('name');
42441              
42442              
42443         }
42444         
42445         if(Roo.isGecko){
42446             this.el.dom.setAttribute('autocomplete', 'off');
42447         }
42448
42449         var cls = 'x-combo-list';
42450
42451         this.list = new Roo.Layer({
42452             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42453         });
42454
42455         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42456         this.list.setWidth(lw);
42457         this.list.swallowEvent('mousewheel');
42458         this.assetHeight = 0;
42459
42460         if(this.title){
42461             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42462             this.assetHeight += this.header.getHeight();
42463         }
42464
42465         this.innerList = this.list.createChild({cls:cls+'-inner'});
42466         this.innerList.on('mouseover', this.onViewOver, this);
42467         this.innerList.on('mousemove', this.onViewMove, this);
42468         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42469         
42470         if(this.allowBlank && !this.pageSize && !this.disableClear){
42471             this.footer = this.list.createChild({cls:cls+'-ft'});
42472             this.pageTb = new Roo.Toolbar(this.footer);
42473            
42474         }
42475         if(this.pageSize){
42476             this.footer = this.list.createChild({cls:cls+'-ft'});
42477             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42478                     {pageSize: this.pageSize});
42479             
42480         }
42481         
42482         if (this.pageTb && this.allowBlank && !this.disableClear) {
42483             var _this = this;
42484             this.pageTb.add(new Roo.Toolbar.Fill(), {
42485                 cls: 'x-btn-icon x-btn-clear',
42486                 text: '&#160;',
42487                 handler: function()
42488                 {
42489                     _this.collapse();
42490                     _this.clearValue();
42491                     _this.onSelect(false, -1);
42492                 }
42493             });
42494         }
42495         if (this.footer) {
42496             this.assetHeight += this.footer.getHeight();
42497         }
42498         
42499
42500         if(!this.tpl){
42501             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42502         }
42503
42504         this.view = new Roo.View(this.innerList, this.tpl, {
42505             singleSelect:true,
42506             store: this.store,
42507             selectedClass: this.selectedClass
42508         });
42509
42510         this.view.on('click', this.onViewClick, this);
42511
42512         this.store.on('beforeload', this.onBeforeLoad, this);
42513         this.store.on('load', this.onLoad, this);
42514         this.store.on('loadexception', this.onLoadException, this);
42515
42516         if(this.resizable){
42517             this.resizer = new Roo.Resizable(this.list,  {
42518                pinned:true, handles:'se'
42519             });
42520             this.resizer.on('resize', function(r, w, h){
42521                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42522                 this.listWidth = w;
42523                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42524                 this.restrictHeight();
42525             }, this);
42526             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42527         }
42528         if(!this.editable){
42529             this.editable = true;
42530             this.setEditable(false);
42531         }  
42532         
42533         
42534         if (typeof(this.events.add.listeners) != 'undefined') {
42535             
42536             this.addicon = this.wrap.createChild(
42537                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42538        
42539             this.addicon.on('click', function(e) {
42540                 this.fireEvent('add', this);
42541             }, this);
42542         }
42543         if (typeof(this.events.edit.listeners) != 'undefined') {
42544             
42545             this.editicon = this.wrap.createChild(
42546                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42547             if (this.addicon) {
42548                 this.editicon.setStyle('margin-left', '40px');
42549             }
42550             this.editicon.on('click', function(e) {
42551                 
42552                 // we fire even  if inothing is selected..
42553                 this.fireEvent('edit', this, this.lastData );
42554                 
42555             }, this);
42556         }
42557         
42558         
42559         
42560     },
42561
42562     // private
42563     initEvents : function(){
42564         Roo.form.ComboBox.superclass.initEvents.call(this);
42565
42566         this.keyNav = new Roo.KeyNav(this.el, {
42567             "up" : function(e){
42568                 this.inKeyMode = true;
42569                 this.selectPrev();
42570             },
42571
42572             "down" : function(e){
42573                 if(!this.isExpanded()){
42574                     this.onTriggerClick();
42575                 }else{
42576                     this.inKeyMode = true;
42577                     this.selectNext();
42578                 }
42579             },
42580
42581             "enter" : function(e){
42582                 this.onViewClick();
42583                 //return true;
42584             },
42585
42586             "esc" : function(e){
42587                 this.collapse();
42588             },
42589
42590             "tab" : function(e){
42591                 this.onViewClick(false);
42592                 this.fireEvent("specialkey", this, e);
42593                 return true;
42594             },
42595
42596             scope : this,
42597
42598             doRelay : function(foo, bar, hname){
42599                 if(hname == 'down' || this.scope.isExpanded()){
42600                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42601                 }
42602                 return true;
42603             },
42604
42605             forceKeyDown: true
42606         });
42607         this.queryDelay = Math.max(this.queryDelay || 10,
42608                 this.mode == 'local' ? 10 : 250);
42609         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42610         if(this.typeAhead){
42611             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42612         }
42613         if(this.editable !== false){
42614             this.el.on("keyup", this.onKeyUp, this);
42615         }
42616         if(this.forceSelection){
42617             this.on('blur', this.doForce, this);
42618         }
42619     },
42620
42621     onDestroy : function(){
42622         if(this.view){
42623             this.view.setStore(null);
42624             this.view.el.removeAllListeners();
42625             this.view.el.remove();
42626             this.view.purgeListeners();
42627         }
42628         if(this.list){
42629             this.list.destroy();
42630         }
42631         if(this.store){
42632             this.store.un('beforeload', this.onBeforeLoad, this);
42633             this.store.un('load', this.onLoad, this);
42634             this.store.un('loadexception', this.onLoadException, this);
42635         }
42636         Roo.form.ComboBox.superclass.onDestroy.call(this);
42637     },
42638
42639     // private
42640     fireKey : function(e){
42641         if(e.isNavKeyPress() && !this.list.isVisible()){
42642             this.fireEvent("specialkey", this, e);
42643         }
42644     },
42645
42646     // private
42647     onResize: function(w, h){
42648         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42649         
42650         if(typeof w != 'number'){
42651             // we do not handle it!?!?
42652             return;
42653         }
42654         var tw = this.trigger.getWidth();
42655         tw += this.addicon ? this.addicon.getWidth() : 0;
42656         tw += this.editicon ? this.editicon.getWidth() : 0;
42657         var x = w - tw;
42658         this.el.setWidth( this.adjustWidth('input', x));
42659             
42660         this.trigger.setStyle('left', x+'px');
42661         
42662         if(this.list && this.listWidth === undefined){
42663             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42664             this.list.setWidth(lw);
42665             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42666         }
42667         
42668     
42669         
42670     },
42671
42672     /**
42673      * Allow or prevent the user from directly editing the field text.  If false is passed,
42674      * the user will only be able to select from the items defined in the dropdown list.  This method
42675      * is the runtime equivalent of setting the 'editable' config option at config time.
42676      * @param {Boolean} value True to allow the user to directly edit the field text
42677      */
42678     setEditable : function(value){
42679         if(value == this.editable){
42680             return;
42681         }
42682         this.editable = value;
42683         if(!value){
42684             this.el.dom.setAttribute('readOnly', true);
42685             this.el.on('mousedown', this.onTriggerClick,  this);
42686             this.el.addClass('x-combo-noedit');
42687         }else{
42688             this.el.dom.setAttribute('readOnly', false);
42689             this.el.un('mousedown', this.onTriggerClick,  this);
42690             this.el.removeClass('x-combo-noedit');
42691         }
42692     },
42693
42694     // private
42695     onBeforeLoad : function(){
42696         if(!this.hasFocus){
42697             return;
42698         }
42699         this.innerList.update(this.loadingText ?
42700                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42701         this.restrictHeight();
42702         this.selectedIndex = -1;
42703     },
42704
42705     // private
42706     onLoad : function(){
42707         if(!this.hasFocus){
42708             return;
42709         }
42710         if(this.store.getCount() > 0){
42711             this.expand();
42712             this.restrictHeight();
42713             if(this.lastQuery == this.allQuery){
42714                 if(this.editable){
42715                     this.el.dom.select();
42716                 }
42717                 if(!this.selectByValue(this.value, true)){
42718                     this.select(0, true);
42719                 }
42720             }else{
42721                 this.selectNext();
42722                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42723                     this.taTask.delay(this.typeAheadDelay);
42724                 }
42725             }
42726         }else{
42727             this.onEmptyResults();
42728         }
42729         //this.el.focus();
42730     },
42731     // private
42732     onLoadException : function()
42733     {
42734         this.collapse();
42735         Roo.log(this.store.reader.jsonData);
42736         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42737             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42738         }
42739         
42740         
42741     },
42742     // private
42743     onTypeAhead : function(){
42744         if(this.store.getCount() > 0){
42745             var r = this.store.getAt(0);
42746             var newValue = r.data[this.displayField];
42747             var len = newValue.length;
42748             var selStart = this.getRawValue().length;
42749             if(selStart != len){
42750                 this.setRawValue(newValue);
42751                 this.selectText(selStart, newValue.length);
42752             }
42753         }
42754     },
42755
42756     // private
42757     onSelect : function(record, index){
42758         if(this.fireEvent('beforeselect', this, record, index) !== false){
42759             this.setFromData(index > -1 ? record.data : false);
42760             this.collapse();
42761             this.fireEvent('select', this, record, index);
42762         }
42763     },
42764
42765     /**
42766      * Returns the currently selected field value or empty string if no value is set.
42767      * @return {String} value The selected value
42768      */
42769     getValue : function(){
42770         if(this.valueField){
42771             return typeof this.value != 'undefined' ? this.value : '';
42772         }
42773         return Roo.form.ComboBox.superclass.getValue.call(this);
42774     },
42775
42776     /**
42777      * Clears any text/value currently set in the field
42778      */
42779     clearValue : function(){
42780         if(this.hiddenField){
42781             this.hiddenField.value = '';
42782         }
42783         this.value = '';
42784         this.setRawValue('');
42785         this.lastSelectionText = '';
42786         
42787     },
42788
42789     /**
42790      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42791      * will be displayed in the field.  If the value does not match the data value of an existing item,
42792      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42793      * Otherwise the field will be blank (although the value will still be set).
42794      * @param {String} value The value to match
42795      */
42796     setValue : function(v){
42797         var text = v;
42798         if(this.valueField){
42799             var r = this.findRecord(this.valueField, v);
42800             if(r){
42801                 text = r.data[this.displayField];
42802             }else if(this.valueNotFoundText !== undefined){
42803                 text = this.valueNotFoundText;
42804             }
42805         }
42806         this.lastSelectionText = text;
42807         if(this.hiddenField){
42808             this.hiddenField.value = v;
42809         }
42810         Roo.form.ComboBox.superclass.setValue.call(this, text);
42811         this.value = v;
42812     },
42813     /**
42814      * @property {Object} the last set data for the element
42815      */
42816     
42817     lastData : false,
42818     /**
42819      * Sets the value of the field based on a object which is related to the record format for the store.
42820      * @param {Object} value the value to set as. or false on reset?
42821      */
42822     setFromData : function(o){
42823         var dv = ''; // display value
42824         var vv = ''; // value value..
42825         this.lastData = o;
42826         if (this.displayField) {
42827             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42828         } else {
42829             // this is an error condition!!!
42830             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42831         }
42832         
42833         if(this.valueField){
42834             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42835         }
42836         if(this.hiddenField){
42837             this.hiddenField.value = vv;
42838             
42839             this.lastSelectionText = dv;
42840             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42841             this.value = vv;
42842             return;
42843         }
42844         // no hidden field.. - we store the value in 'value', but still display
42845         // display field!!!!
42846         this.lastSelectionText = dv;
42847         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42848         this.value = vv;
42849         
42850         
42851     },
42852     // private
42853     reset : function(){
42854         // overridden so that last data is reset..
42855         this.setValue(this.resetValue);
42856         this.originalValue = this.getValue();
42857         this.clearInvalid();
42858         this.lastData = false;
42859         if (this.view) {
42860             this.view.clearSelections();
42861         }
42862     },
42863     // private
42864     findRecord : function(prop, value){
42865         var record;
42866         if(this.store.getCount() > 0){
42867             this.store.each(function(r){
42868                 if(r.data[prop] == value){
42869                     record = r;
42870                     return false;
42871                 }
42872                 return true;
42873             });
42874         }
42875         return record;
42876     },
42877     
42878     getName: function()
42879     {
42880         // returns hidden if it's set..
42881         if (!this.rendered) {return ''};
42882         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42883         
42884     },
42885     // private
42886     onViewMove : function(e, t){
42887         this.inKeyMode = false;
42888     },
42889
42890     // private
42891     onViewOver : function(e, t){
42892         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42893             return;
42894         }
42895         var item = this.view.findItemFromChild(t);
42896         if(item){
42897             var index = this.view.indexOf(item);
42898             this.select(index, false);
42899         }
42900     },
42901
42902     // private
42903     onViewClick : function(doFocus)
42904     {
42905         var index = this.view.getSelectedIndexes()[0];
42906         var r = this.store.getAt(index);
42907         if(r){
42908             this.onSelect(r, index);
42909         }
42910         if(doFocus !== false && !this.blockFocus){
42911             this.el.focus();
42912         }
42913     },
42914
42915     // private
42916     restrictHeight : function(){
42917         this.innerList.dom.style.height = '';
42918         var inner = this.innerList.dom;
42919         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42920         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42921         this.list.beginUpdate();
42922         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42923         this.list.alignTo(this.el, this.listAlign);
42924         this.list.endUpdate();
42925     },
42926
42927     // private
42928     onEmptyResults : function(){
42929         this.collapse();
42930     },
42931
42932     /**
42933      * Returns true if the dropdown list is expanded, else false.
42934      */
42935     isExpanded : function(){
42936         return this.list.isVisible();
42937     },
42938
42939     /**
42940      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42941      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42942      * @param {String} value The data value of the item to select
42943      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42944      * selected item if it is not currently in view (defaults to true)
42945      * @return {Boolean} True if the value matched an item in the list, else false
42946      */
42947     selectByValue : function(v, scrollIntoView){
42948         if(v !== undefined && v !== null){
42949             var r = this.findRecord(this.valueField || this.displayField, v);
42950             if(r){
42951                 this.select(this.store.indexOf(r), scrollIntoView);
42952                 return true;
42953             }
42954         }
42955         return false;
42956     },
42957
42958     /**
42959      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42960      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42961      * @param {Number} index The zero-based index of the list item to select
42962      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42963      * selected item if it is not currently in view (defaults to true)
42964      */
42965     select : function(index, scrollIntoView){
42966         this.selectedIndex = index;
42967         this.view.select(index);
42968         if(scrollIntoView !== false){
42969             var el = this.view.getNode(index);
42970             if(el){
42971                 this.innerList.scrollChildIntoView(el, false);
42972             }
42973         }
42974     },
42975
42976     // private
42977     selectNext : function(){
42978         var ct = this.store.getCount();
42979         if(ct > 0){
42980             if(this.selectedIndex == -1){
42981                 this.select(0);
42982             }else if(this.selectedIndex < ct-1){
42983                 this.select(this.selectedIndex+1);
42984             }
42985         }
42986     },
42987
42988     // private
42989     selectPrev : function(){
42990         var ct = this.store.getCount();
42991         if(ct > 0){
42992             if(this.selectedIndex == -1){
42993                 this.select(0);
42994             }else if(this.selectedIndex != 0){
42995                 this.select(this.selectedIndex-1);
42996             }
42997         }
42998     },
42999
43000     // private
43001     onKeyUp : function(e){
43002         if(this.editable !== false && !e.isSpecialKey()){
43003             this.lastKey = e.getKey();
43004             this.dqTask.delay(this.queryDelay);
43005         }
43006     },
43007
43008     // private
43009     validateBlur : function(){
43010         return !this.list || !this.list.isVisible();   
43011     },
43012
43013     // private
43014     initQuery : function(){
43015         this.doQuery(this.getRawValue());
43016     },
43017
43018     // private
43019     doForce : function(){
43020         if(this.el.dom.value.length > 0){
43021             this.el.dom.value =
43022                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43023              
43024         }
43025     },
43026
43027     /**
43028      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43029      * query allowing the query action to be canceled if needed.
43030      * @param {String} query The SQL query to execute
43031      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43032      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43033      * saved in the current store (defaults to false)
43034      */
43035     doQuery : function(q, forceAll){
43036         if(q === undefined || q === null){
43037             q = '';
43038         }
43039         var qe = {
43040             query: q,
43041             forceAll: forceAll,
43042             combo: this,
43043             cancel:false
43044         };
43045         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43046             return false;
43047         }
43048         q = qe.query;
43049         forceAll = qe.forceAll;
43050         if(forceAll === true || (q.length >= this.minChars)){
43051             if(this.lastQuery != q || this.alwaysQuery){
43052                 this.lastQuery = q;
43053                 if(this.mode == 'local'){
43054                     this.selectedIndex = -1;
43055                     if(forceAll){
43056                         this.store.clearFilter();
43057                     }else{
43058                         this.store.filter(this.displayField, q);
43059                     }
43060                     this.onLoad();
43061                 }else{
43062                     this.store.baseParams[this.queryParam] = q;
43063                     this.store.load({
43064                         params: this.getParams(q)
43065                     });
43066                     this.expand();
43067                 }
43068             }else{
43069                 this.selectedIndex = -1;
43070                 this.onLoad();   
43071             }
43072         }
43073     },
43074
43075     // private
43076     getParams : function(q){
43077         var p = {};
43078         //p[this.queryParam] = q;
43079         if(this.pageSize){
43080             p.start = 0;
43081             p.limit = this.pageSize;
43082         }
43083         return p;
43084     },
43085
43086     /**
43087      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43088      */
43089     collapse : function(){
43090         if(!this.isExpanded()){
43091             return;
43092         }
43093         this.list.hide();
43094         Roo.get(document).un('mousedown', this.collapseIf, this);
43095         Roo.get(document).un('mousewheel', this.collapseIf, this);
43096         if (!this.editable) {
43097             Roo.get(document).un('keydown', this.listKeyPress, this);
43098         }
43099         this.fireEvent('collapse', this);
43100     },
43101
43102     // private
43103     collapseIf : function(e){
43104         if(!e.within(this.wrap) && !e.within(this.list)){
43105             this.collapse();
43106         }
43107     },
43108
43109     /**
43110      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43111      */
43112     expand : function(){
43113         if(this.isExpanded() || !this.hasFocus){
43114             return;
43115         }
43116         this.list.alignTo(this.el, this.listAlign);
43117         this.list.show();
43118         Roo.get(document).on('mousedown', this.collapseIf, this);
43119         Roo.get(document).on('mousewheel', this.collapseIf, this);
43120         if (!this.editable) {
43121             Roo.get(document).on('keydown', this.listKeyPress, this);
43122         }
43123         
43124         this.fireEvent('expand', this);
43125     },
43126
43127     // private
43128     // Implements the default empty TriggerField.onTriggerClick function
43129     onTriggerClick : function(){
43130         if(this.disabled){
43131             return;
43132         }
43133         if(this.isExpanded()){
43134             this.collapse();
43135             if (!this.blockFocus) {
43136                 this.el.focus();
43137             }
43138             
43139         }else {
43140             this.hasFocus = true;
43141             if(this.triggerAction == 'all') {
43142                 this.doQuery(this.allQuery, true);
43143             } else {
43144                 this.doQuery(this.getRawValue());
43145             }
43146             if (!this.blockFocus) {
43147                 this.el.focus();
43148             }
43149         }
43150     },
43151     listKeyPress : function(e)
43152     {
43153         //Roo.log('listkeypress');
43154         // scroll to first matching element based on key pres..
43155         if (e.isSpecialKey()) {
43156             return false;
43157         }
43158         var k = String.fromCharCode(e.getKey()).toUpperCase();
43159         //Roo.log(k);
43160         var match  = false;
43161         var csel = this.view.getSelectedNodes();
43162         var cselitem = false;
43163         if (csel.length) {
43164             var ix = this.view.indexOf(csel[0]);
43165             cselitem  = this.store.getAt(ix);
43166             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43167                 cselitem = false;
43168             }
43169             
43170         }
43171         
43172         this.store.each(function(v) { 
43173             if (cselitem) {
43174                 // start at existing selection.
43175                 if (cselitem.id == v.id) {
43176                     cselitem = false;
43177                 }
43178                 return;
43179             }
43180                 
43181             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43182                 match = this.store.indexOf(v);
43183                 return false;
43184             }
43185         }, this);
43186         
43187         if (match === false) {
43188             return true; // no more action?
43189         }
43190         // scroll to?
43191         this.view.select(match);
43192         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43193         sn.scrollIntoView(sn.dom.parentNode, false);
43194     } 
43195
43196     /** 
43197     * @cfg {Boolean} grow 
43198     * @hide 
43199     */
43200     /** 
43201     * @cfg {Number} growMin 
43202     * @hide 
43203     */
43204     /** 
43205     * @cfg {Number} growMax 
43206     * @hide 
43207     */
43208     /**
43209      * @hide
43210      * @method autoSize
43211      */
43212 });/*
43213  * Copyright(c) 2010-2012, Roo J Solutions Limited
43214  *
43215  * Licence LGPL
43216  *
43217  */
43218
43219 /**
43220  * @class Roo.form.ComboBoxArray
43221  * @extends Roo.form.TextField
43222  * A facebook style adder... for lists of email / people / countries  etc...
43223  * pick multiple items from a combo box, and shows each one.
43224  *
43225  *  Fred [x]  Brian [x]  [Pick another |v]
43226  *
43227  *
43228  *  For this to work: it needs various extra information
43229  *    - normal combo problay has
43230  *      name, hiddenName
43231  *    + displayField, valueField
43232  *
43233  *    For our purpose...
43234  *
43235  *
43236  *   If we change from 'extends' to wrapping...
43237  *   
43238  *  
43239  *
43240  
43241  
43242  * @constructor
43243  * Create a new ComboBoxArray.
43244  * @param {Object} config Configuration options
43245  */
43246  
43247
43248 Roo.form.ComboBoxArray = function(config)
43249 {
43250     this.addEvents({
43251         /**
43252          * @event beforeremove
43253          * Fires before remove the value from the list
43254              * @param {Roo.form.ComboBoxArray} _self This combo box array
43255              * @param {Roo.form.ComboBoxArray.Item} item removed item
43256              */
43257         'beforeremove' : true,
43258         /**
43259          * @event remove
43260          * Fires when remove the value from the list
43261              * @param {Roo.form.ComboBoxArray} _self This combo box array
43262              * @param {Roo.form.ComboBoxArray.Item} item removed item
43263              */
43264         'remove' : true
43265         
43266         
43267     });
43268     
43269     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43270     
43271     this.items = new Roo.util.MixedCollection(false);
43272     
43273     // construct the child combo...
43274     
43275     
43276     
43277     
43278    
43279     
43280 }
43281
43282  
43283 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43284
43285     /**
43286      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43287      */
43288     
43289     lastData : false,
43290     
43291     // behavies liek a hiddne field
43292     inputType:      'hidden',
43293     /**
43294      * @cfg {Number} width The width of the box that displays the selected element
43295      */ 
43296     width:          300,
43297
43298     
43299     
43300     /**
43301      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43302      */
43303     name : false,
43304     /**
43305      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43306      */
43307     hiddenName : false,
43308       /**
43309      * @cfg {String} seperator    The value seperator normally ',' 
43310      */
43311     seperator : ',',
43312     
43313     // private the array of items that are displayed..
43314     items  : false,
43315     // private - the hidden field el.
43316     hiddenEl : false,
43317     // private - the filed el..
43318     el : false,
43319     
43320     //validateValue : function() { return true; }, // all values are ok!
43321     //onAddClick: function() { },
43322     
43323     onRender : function(ct, position) 
43324     {
43325         
43326         // create the standard hidden element
43327         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43328         
43329         
43330         // give fake names to child combo;
43331         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43332         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43333         
43334         this.combo = Roo.factory(this.combo, Roo.form);
43335         this.combo.onRender(ct, position);
43336         if (typeof(this.combo.width) != 'undefined') {
43337             this.combo.onResize(this.combo.width,0);
43338         }
43339         
43340         this.combo.initEvents();
43341         
43342         // assigned so form know we need to do this..
43343         this.store          = this.combo.store;
43344         this.valueField     = this.combo.valueField;
43345         this.displayField   = this.combo.displayField ;
43346         
43347         
43348         this.combo.wrap.addClass('x-cbarray-grp');
43349         
43350         var cbwrap = this.combo.wrap.createChild(
43351             {tag: 'div', cls: 'x-cbarray-cb'},
43352             this.combo.el.dom
43353         );
43354         
43355              
43356         this.hiddenEl = this.combo.wrap.createChild({
43357             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43358         });
43359         this.el = this.combo.wrap.createChild({
43360             tag: 'input',  type:'hidden' , name: this.name, value : ''
43361         });
43362          //   this.el.dom.removeAttribute("name");
43363         
43364         
43365         this.outerWrap = this.combo.wrap;
43366         this.wrap = cbwrap;
43367         
43368         this.outerWrap.setWidth(this.width);
43369         this.outerWrap.dom.removeChild(this.el.dom);
43370         
43371         this.wrap.dom.appendChild(this.el.dom);
43372         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43373         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43374         
43375         this.combo.trigger.setStyle('position','relative');
43376         this.combo.trigger.setStyle('left', '0px');
43377         this.combo.trigger.setStyle('top', '2px');
43378         
43379         this.combo.el.setStyle('vertical-align', 'text-bottom');
43380         
43381         //this.trigger.setStyle('vertical-align', 'top');
43382         
43383         // this should use the code from combo really... on('add' ....)
43384         if (this.adder) {
43385             
43386         
43387             this.adder = this.outerWrap.createChild(
43388                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43389             var _t = this;
43390             this.adder.on('click', function(e) {
43391                 _t.fireEvent('adderclick', this, e);
43392             }, _t);
43393         }
43394         //var _t = this;
43395         //this.adder.on('click', this.onAddClick, _t);
43396         
43397         
43398         this.combo.on('select', function(cb, rec, ix) {
43399             this.addItem(rec.data);
43400             
43401             cb.setValue('');
43402             cb.el.dom.value = '';
43403             //cb.lastData = rec.data;
43404             // add to list
43405             
43406         }, this);
43407         
43408         
43409     },
43410     
43411     
43412     getName: function()
43413     {
43414         // returns hidden if it's set..
43415         if (!this.rendered) {return ''};
43416         return  this.hiddenName ? this.hiddenName : this.name;
43417         
43418     },
43419     
43420     
43421     onResize: function(w, h){
43422         
43423         return;
43424         // not sure if this is needed..
43425         //this.combo.onResize(w,h);
43426         
43427         if(typeof w != 'number'){
43428             // we do not handle it!?!?
43429             return;
43430         }
43431         var tw = this.combo.trigger.getWidth();
43432         tw += this.addicon ? this.addicon.getWidth() : 0;
43433         tw += this.editicon ? this.editicon.getWidth() : 0;
43434         var x = w - tw;
43435         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43436             
43437         this.combo.trigger.setStyle('left', '0px');
43438         
43439         if(this.list && this.listWidth === undefined){
43440             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43441             this.list.setWidth(lw);
43442             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43443         }
43444         
43445     
43446         
43447     },
43448     
43449     addItem: function(rec)
43450     {
43451         var valueField = this.combo.valueField;
43452         var displayField = this.combo.displayField;
43453         
43454         if (this.items.indexOfKey(rec[valueField]) > -1) {
43455             //console.log("GOT " + rec.data.id);
43456             return;
43457         }
43458         
43459         var x = new Roo.form.ComboBoxArray.Item({
43460             //id : rec[this.idField],
43461             data : rec,
43462             displayField : displayField ,
43463             tipField : displayField ,
43464             cb : this
43465         });
43466         // use the 
43467         this.items.add(rec[valueField],x);
43468         // add it before the element..
43469         this.updateHiddenEl();
43470         x.render(this.outerWrap, this.wrap.dom);
43471         // add the image handler..
43472     },
43473     
43474     updateHiddenEl : function()
43475     {
43476         this.validate();
43477         if (!this.hiddenEl) {
43478             return;
43479         }
43480         var ar = [];
43481         var idField = this.combo.valueField;
43482         
43483         this.items.each(function(f) {
43484             ar.push(f.data[idField]);
43485         });
43486         this.hiddenEl.dom.value = ar.join(this.seperator);
43487         this.validate();
43488     },
43489     
43490     reset : function()
43491     {
43492         this.items.clear();
43493         
43494         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43495            el.remove();
43496         });
43497         
43498         this.el.dom.value = '';
43499         if (this.hiddenEl) {
43500             this.hiddenEl.dom.value = '';
43501         }
43502         
43503     },
43504     getValue: function()
43505     {
43506         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43507     },
43508     setValue: function(v) // not a valid action - must use addItems..
43509     {
43510         
43511         this.reset();
43512          
43513         if (this.store.isLocal && (typeof(v) == 'string')) {
43514             // then we can use the store to find the values..
43515             // comma seperated at present.. this needs to allow JSON based encoding..
43516             this.hiddenEl.value  = v;
43517             var v_ar = [];
43518             Roo.each(v.split(this.seperator), function(k) {
43519                 Roo.log("CHECK " + this.valueField + ',' + k);
43520                 var li = this.store.query(this.valueField, k);
43521                 if (!li.length) {
43522                     return;
43523                 }
43524                 var add = {};
43525                 add[this.valueField] = k;
43526                 add[this.displayField] = li.item(0).data[this.displayField];
43527                 
43528                 this.addItem(add);
43529             }, this) 
43530              
43531         }
43532         if (typeof(v) == 'object' ) {
43533             // then let's assume it's an array of objects..
43534             Roo.each(v, function(l) {
43535                 var add = l;
43536                 if (typeof(l) == 'string') {
43537                     add = {};
43538                     add[this.valueField] = l;
43539                     add[this.displayField] = l
43540                 }
43541                 this.addItem(add);
43542             }, this);
43543              
43544         }
43545         
43546         
43547     },
43548     setFromData: function(v)
43549     {
43550         // this recieves an object, if setValues is called.
43551         this.reset();
43552         this.el.dom.value = v[this.displayField];
43553         this.hiddenEl.dom.value = v[this.valueField];
43554         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43555             return;
43556         }
43557         var kv = v[this.valueField];
43558         var dv = v[this.displayField];
43559         kv = typeof(kv) != 'string' ? '' : kv;
43560         dv = typeof(dv) != 'string' ? '' : dv;
43561         
43562         
43563         var keys = kv.split(this.seperator);
43564         var display = dv.split(this.seperator);
43565         for (var i = 0 ; i < keys.length; i++) {
43566             add = {};
43567             add[this.valueField] = keys[i];
43568             add[this.displayField] = display[i];
43569             this.addItem(add);
43570         }
43571       
43572         
43573     },
43574     
43575     /**
43576      * Validates the combox array value
43577      * @return {Boolean} True if the value is valid, else false
43578      */
43579     validate : function(){
43580         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43581             this.clearInvalid();
43582             return true;
43583         }
43584         return false;
43585     },
43586     
43587     validateValue : function(value){
43588         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43589         
43590     },
43591     
43592     /*@
43593      * overide
43594      * 
43595      */
43596     isDirty : function() {
43597         if(this.disabled) {
43598             return false;
43599         }
43600         
43601         try {
43602             var d = Roo.decode(String(this.originalValue));
43603         } catch (e) {
43604             return String(this.getValue()) !== String(this.originalValue);
43605         }
43606         
43607         var originalValue = [];
43608         
43609         for (var i = 0; i < d.length; i++){
43610             originalValue.push(d[i][this.valueField]);
43611         }
43612         
43613         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43614         
43615     }
43616     
43617 });
43618
43619
43620
43621 /**
43622  * @class Roo.form.ComboBoxArray.Item
43623  * @extends Roo.BoxComponent
43624  * A selected item in the list
43625  *  Fred [x]  Brian [x]  [Pick another |v]
43626  * 
43627  * @constructor
43628  * Create a new item.
43629  * @param {Object} config Configuration options
43630  */
43631  
43632 Roo.form.ComboBoxArray.Item = function(config) {
43633     config.id = Roo.id();
43634     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43635 }
43636
43637 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43638     data : {},
43639     cb: false,
43640     displayField : false,
43641     tipField : false,
43642     
43643     
43644     defaultAutoCreate : {
43645         tag: 'div',
43646         cls: 'x-cbarray-item',
43647         cn : [ 
43648             { tag: 'div' },
43649             {
43650                 tag: 'img',
43651                 width:16,
43652                 height : 16,
43653                 src : Roo.BLANK_IMAGE_URL ,
43654                 align: 'center'
43655             }
43656         ]
43657         
43658     },
43659     
43660  
43661     onRender : function(ct, position)
43662     {
43663         Roo.form.Field.superclass.onRender.call(this, ct, position);
43664         
43665         if(!this.el){
43666             var cfg = this.getAutoCreate();
43667             this.el = ct.createChild(cfg, position);
43668         }
43669         
43670         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43671         
43672         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43673             this.cb.renderer(this.data) :
43674             String.format('{0}',this.data[this.displayField]);
43675         
43676             
43677         this.el.child('div').dom.setAttribute('qtip',
43678                         String.format('{0}',this.data[this.tipField])
43679         );
43680         
43681         this.el.child('img').on('click', this.remove, this);
43682         
43683     },
43684    
43685     remove : function()
43686     {
43687         if(this.cb.disabled){
43688             return;
43689         }
43690         
43691         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43692             this.cb.items.remove(this);
43693             this.el.child('img').un('click', this.remove, this);
43694             this.el.remove();
43695             this.cb.updateHiddenEl();
43696
43697             this.cb.fireEvent('remove', this.cb, this);
43698         }
43699         
43700     }
43701 });/*
43702  * RooJS Library 1.1.1
43703  * Copyright(c) 2008-2011  Alan Knowles
43704  *
43705  * License - LGPL
43706  */
43707  
43708
43709 /**
43710  * @class Roo.form.ComboNested
43711  * @extends Roo.form.ComboBox
43712  * A combobox for that allows selection of nested items in a list,
43713  * eg.
43714  *
43715  *  Book
43716  *    -> red
43717  *    -> green
43718  *  Table
43719  *    -> square
43720  *      ->red
43721  *      ->green
43722  *    -> rectangle
43723  *      ->green
43724  *      
43725  * 
43726  * @constructor
43727  * Create a new ComboNested
43728  * @param {Object} config Configuration options
43729  */
43730 Roo.form.ComboNested = function(config){
43731     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43732     // should verify some data...
43733     // like
43734     // hiddenName = required..
43735     // displayField = required
43736     // valudField == required
43737     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43738     var _t = this;
43739     Roo.each(req, function(e) {
43740         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43741             throw "Roo.form.ComboNested : missing value for: " + e;
43742         }
43743     });
43744      
43745     
43746 };
43747
43748 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43749    
43750     /*
43751      * @config {Number} max Number of columns to show
43752      */
43753     
43754     maxColumns : 3,
43755    
43756     list : null, // the outermost div..
43757     innerLists : null, // the
43758     views : null,
43759     stores : null,
43760     // private
43761     loadingChildren : false,
43762     
43763     onRender : function(ct, position)
43764     {
43765         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43766         
43767         if(this.hiddenName){
43768             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43769                     'before', true);
43770             this.hiddenField.value =
43771                 this.hiddenValue !== undefined ? this.hiddenValue :
43772                 this.value !== undefined ? this.value : '';
43773
43774             // prevent input submission
43775             this.el.dom.removeAttribute('name');
43776              
43777              
43778         }
43779         
43780         if(Roo.isGecko){
43781             this.el.dom.setAttribute('autocomplete', 'off');
43782         }
43783
43784         var cls = 'x-combo-list';
43785
43786         this.list = new Roo.Layer({
43787             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43788         });
43789
43790         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43791         this.list.setWidth(lw);
43792         this.list.swallowEvent('mousewheel');
43793         this.assetHeight = 0;
43794
43795         if(this.title){
43796             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43797             this.assetHeight += this.header.getHeight();
43798         }
43799         this.innerLists = [];
43800         this.views = [];
43801         this.stores = [];
43802         for (var i =0 ; i < this.maxColumns; i++) {
43803             this.onRenderList( cls, i);
43804         }
43805         
43806         // always needs footer, as we are going to have an 'OK' button.
43807         this.footer = this.list.createChild({cls:cls+'-ft'});
43808         this.pageTb = new Roo.Toolbar(this.footer);  
43809         var _this = this;
43810         this.pageTb.add(  {
43811             
43812             text: 'Done',
43813             handler: function()
43814             {
43815                 _this.collapse();
43816             }
43817         });
43818         
43819         if ( this.allowBlank && !this.disableClear) {
43820             
43821             this.pageTb.add(new Roo.Toolbar.Fill(), {
43822                 cls: 'x-btn-icon x-btn-clear',
43823                 text: '&#160;',
43824                 handler: function()
43825                 {
43826                     _this.collapse();
43827                     _this.clearValue();
43828                     _this.onSelect(false, -1);
43829                 }
43830             });
43831         }
43832         if (this.footer) {
43833             this.assetHeight += this.footer.getHeight();
43834         }
43835         
43836     },
43837     onRenderList : function (  cls, i)
43838     {
43839         
43840         var lw = Math.floor(
43841                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43842         );
43843         
43844         this.list.setWidth(lw); // default to '1'
43845
43846         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43847         //il.on('mouseover', this.onViewOver, this, { list:  i });
43848         //il.on('mousemove', this.onViewMove, this, { list:  i });
43849         il.setWidth(lw);
43850         il.setStyle({ 'overflow-x' : 'hidden'});
43851
43852         if(!this.tpl){
43853             this.tpl = new Roo.Template({
43854                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43855                 isEmpty: function (value, allValues) {
43856                     //Roo.log(value);
43857                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43858                     return dl ? 'has-children' : 'no-children'
43859                 }
43860             });
43861         }
43862         
43863         var store  = this.store;
43864         if (i > 0) {
43865             store  = new Roo.data.SimpleStore({
43866                 //fields : this.store.reader.meta.fields,
43867                 reader : this.store.reader,
43868                 data : [ ]
43869             });
43870         }
43871         this.stores[i]  = store;
43872                   
43873         var view = this.views[i] = new Roo.View(
43874             il,
43875             this.tpl,
43876             {
43877                 singleSelect:true,
43878                 store: store,
43879                 selectedClass: this.selectedClass
43880             }
43881         );
43882         view.getEl().setWidth(lw);
43883         view.getEl().setStyle({
43884             position: i < 1 ? 'relative' : 'absolute',
43885             top: 0,
43886             left: (i * lw ) + 'px',
43887             display : i > 0 ? 'none' : 'block'
43888         });
43889         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43890         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43891         //view.on('click', this.onViewClick, this, { list : i });
43892
43893         store.on('beforeload', this.onBeforeLoad, this);
43894         store.on('load',  this.onLoad, this, { list  : i});
43895         store.on('loadexception', this.onLoadException, this);
43896
43897         // hide the other vies..
43898         
43899         
43900         
43901     },
43902       
43903     restrictHeight : function()
43904     {
43905         var mh = 0;
43906         Roo.each(this.innerLists, function(il,i) {
43907             var el = this.views[i].getEl();
43908             el.dom.style.height = '';
43909             var inner = el.dom;
43910             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43911             // only adjust heights on other ones..
43912             mh = Math.max(h, mh);
43913             if (i < 1) {
43914                 
43915                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43916                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43917                
43918             }
43919             
43920             
43921         }, this);
43922         
43923         this.list.beginUpdate();
43924         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43925         this.list.alignTo(this.el, this.listAlign);
43926         this.list.endUpdate();
43927         
43928     },
43929      
43930     
43931     // -- store handlers..
43932     // private
43933     onBeforeLoad : function()
43934     {
43935         if(!this.hasFocus){
43936             return;
43937         }
43938         this.innerLists[0].update(this.loadingText ?
43939                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43940         this.restrictHeight();
43941         this.selectedIndex = -1;
43942     },
43943     // private
43944     onLoad : function(a,b,c,d)
43945     {
43946         if (!this.loadingChildren) {
43947             // then we are loading the top level. - hide the children
43948             for (var i = 1;i < this.views.length; i++) {
43949                 this.views[i].getEl().setStyle({ display : 'none' });
43950             }
43951             var lw = Math.floor(
43952                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43953             );
43954         
43955              this.list.setWidth(lw); // default to '1'
43956
43957             
43958         }
43959         if(!this.hasFocus){
43960             return;
43961         }
43962         
43963         if(this.store.getCount() > 0) {
43964             this.expand();
43965             this.restrictHeight();   
43966         } else {
43967             this.onEmptyResults();
43968         }
43969         
43970         if (!this.loadingChildren) {
43971             this.selectActive();
43972         }
43973         /*
43974         this.stores[1].loadData([]);
43975         this.stores[2].loadData([]);
43976         this.views
43977         */    
43978     
43979         //this.el.focus();
43980     },
43981     
43982     
43983     // private
43984     onLoadException : function()
43985     {
43986         this.collapse();
43987         Roo.log(this.store.reader.jsonData);
43988         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43989             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43990         }
43991         
43992         
43993     },
43994     // no cleaning of leading spaces on blur here.
43995     cleanLeadingSpace : function(e) { },
43996     
43997
43998     onSelectChange : function (view, sels, opts )
43999     {
44000         var ix = view.getSelectedIndexes();
44001          
44002         if (opts.list > this.maxColumns - 2) {
44003             if (view.store.getCount()<  1) {
44004                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44005
44006             } else  {
44007                 if (ix.length) {
44008                     // used to clear ?? but if we are loading unselected 
44009                     this.setFromData(view.store.getAt(ix[0]).data);
44010                 }
44011                 
44012             }
44013             
44014             return;
44015         }
44016         
44017         if (!ix.length) {
44018             // this get's fired when trigger opens..
44019            // this.setFromData({});
44020             var str = this.stores[opts.list+1];
44021             str.data.clear(); // removeall wihtout the fire events..
44022             return;
44023         }
44024         
44025         var rec = view.store.getAt(ix[0]);
44026          
44027         this.setFromData(rec.data);
44028         this.fireEvent('select', this, rec, ix[0]);
44029         
44030         var lw = Math.floor(
44031              (
44032                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44033              ) / this.maxColumns
44034         );
44035         this.loadingChildren = true;
44036         this.stores[opts.list+1].loadDataFromChildren( rec );
44037         this.loadingChildren = false;
44038         var dl = this.stores[opts.list+1]. getTotalCount();
44039         
44040         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44041         
44042         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44043         for (var i = opts.list+2; i < this.views.length;i++) {
44044             this.views[i].getEl().setStyle({ display : 'none' });
44045         }
44046         
44047         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44048         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44049         
44050         if (this.isLoading) {
44051            // this.selectActive(opts.list);
44052         }
44053          
44054     },
44055     
44056     
44057     
44058     
44059     onDoubleClick : function()
44060     {
44061         this.collapse(); //??
44062     },
44063     
44064      
44065     
44066     
44067     
44068     // private
44069     recordToStack : function(store, prop, value, stack)
44070     {
44071         var cstore = new Roo.data.SimpleStore({
44072             //fields : this.store.reader.meta.fields, // we need array reader.. for
44073             reader : this.store.reader,
44074             data : [ ]
44075         });
44076         var _this = this;
44077         var record  = false;
44078         var srec = false;
44079         if(store.getCount() < 1){
44080             return false;
44081         }
44082         store.each(function(r){
44083             if(r.data[prop] == value){
44084                 record = r;
44085             srec = r;
44086                 return false;
44087             }
44088             if (r.data.cn && r.data.cn.length) {
44089                 cstore.loadDataFromChildren( r);
44090                 var cret = _this.recordToStack(cstore, prop, value, stack);
44091                 if (cret !== false) {
44092                     record = cret;
44093                     srec = r;
44094                     return false;
44095                 }
44096             }
44097              
44098             return true;
44099         });
44100         if (record == false) {
44101             return false
44102         }
44103         stack.unshift(srec);
44104         return record;
44105     },
44106     
44107     /*
44108      * find the stack of stores that match our value.
44109      *
44110      * 
44111      */
44112     
44113     selectActive : function ()
44114     {
44115         // if store is not loaded, then we will need to wait for that to happen first.
44116         var stack = [];
44117         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44118         for (var i = 0; i < stack.length; i++ ) {
44119             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44120         }
44121         
44122     }
44123         
44124          
44125     
44126     
44127     
44128     
44129 });/*
44130  * Based on:
44131  * Ext JS Library 1.1.1
44132  * Copyright(c) 2006-2007, Ext JS, LLC.
44133  *
44134  * Originally Released Under LGPL - original licence link has changed is not relivant.
44135  *
44136  * Fork - LGPL
44137  * <script type="text/javascript">
44138  */
44139 /**
44140  * @class Roo.form.Checkbox
44141  * @extends Roo.form.Field
44142  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44143  * @constructor
44144  * Creates a new Checkbox
44145  * @param {Object} config Configuration options
44146  */
44147 Roo.form.Checkbox = function(config){
44148     Roo.form.Checkbox.superclass.constructor.call(this, config);
44149     this.addEvents({
44150         /**
44151          * @event check
44152          * Fires when the checkbox is checked or unchecked.
44153              * @param {Roo.form.Checkbox} this This checkbox
44154              * @param {Boolean} checked The new checked value
44155              */
44156         check : true
44157     });
44158 };
44159
44160 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44161     /**
44162      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44163      */
44164     focusClass : undefined,
44165     /**
44166      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44167      */
44168     fieldClass: "x-form-field",
44169     /**
44170      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44171      */
44172     checked: false,
44173     /**
44174      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44175      * {tag: "input", type: "checkbox", autocomplete: "off"})
44176      */
44177     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44178     /**
44179      * @cfg {String} boxLabel The text that appears beside the checkbox
44180      */
44181     boxLabel : "",
44182     /**
44183      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44184      */  
44185     inputValue : '1',
44186     /**
44187      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44188      */
44189      valueOff: '0', // value when not checked..
44190
44191     actionMode : 'viewEl', 
44192     //
44193     // private
44194     itemCls : 'x-menu-check-item x-form-item',
44195     groupClass : 'x-menu-group-item',
44196     inputType : 'hidden',
44197     
44198     
44199     inSetChecked: false, // check that we are not calling self...
44200     
44201     inputElement: false, // real input element?
44202     basedOn: false, // ????
44203     
44204     isFormField: true, // not sure where this is needed!!!!
44205
44206     onResize : function(){
44207         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44208         if(!this.boxLabel){
44209             this.el.alignTo(this.wrap, 'c-c');
44210         }
44211     },
44212
44213     initEvents : function(){
44214         Roo.form.Checkbox.superclass.initEvents.call(this);
44215         this.el.on("click", this.onClick,  this);
44216         this.el.on("change", this.onClick,  this);
44217     },
44218
44219
44220     getResizeEl : function(){
44221         return this.wrap;
44222     },
44223
44224     getPositionEl : function(){
44225         return this.wrap;
44226     },
44227
44228     // private
44229     onRender : function(ct, position){
44230         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44231         /*
44232         if(this.inputValue !== undefined){
44233             this.el.dom.value = this.inputValue;
44234         }
44235         */
44236         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44237         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44238         var viewEl = this.wrap.createChild({ 
44239             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44240         this.viewEl = viewEl;   
44241         this.wrap.on('click', this.onClick,  this); 
44242         
44243         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44244         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44245         
44246         
44247         
44248         if(this.boxLabel){
44249             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44250         //    viewEl.on('click', this.onClick,  this); 
44251         }
44252         //if(this.checked){
44253             this.setChecked(this.checked);
44254         //}else{
44255             //this.checked = this.el.dom;
44256         //}
44257
44258     },
44259
44260     // private
44261     initValue : Roo.emptyFn,
44262
44263     /**
44264      * Returns the checked state of the checkbox.
44265      * @return {Boolean} True if checked, else false
44266      */
44267     getValue : function(){
44268         if(this.el){
44269             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44270         }
44271         return this.valueOff;
44272         
44273     },
44274
44275         // private
44276     onClick : function(){ 
44277         if (this.disabled) {
44278             return;
44279         }
44280         this.setChecked(!this.checked);
44281
44282         //if(this.el.dom.checked != this.checked){
44283         //    this.setValue(this.el.dom.checked);
44284        // }
44285     },
44286
44287     /**
44288      * Sets the checked state of the checkbox.
44289      * On is always based on a string comparison between inputValue and the param.
44290      * @param {Boolean/String} value - the value to set 
44291      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44292      */
44293     setValue : function(v,suppressEvent){
44294         
44295         
44296         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44297         //if(this.el && this.el.dom){
44298         //    this.el.dom.checked = this.checked;
44299         //    this.el.dom.defaultChecked = this.checked;
44300         //}
44301         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44302         //this.fireEvent("check", this, this.checked);
44303     },
44304     // private..
44305     setChecked : function(state,suppressEvent)
44306     {
44307         if (this.inSetChecked) {
44308             this.checked = state;
44309             return;
44310         }
44311         
44312     
44313         if(this.wrap){
44314             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44315         }
44316         this.checked = state;
44317         if(suppressEvent !== true){
44318             this.fireEvent('check', this, state);
44319         }
44320         this.inSetChecked = true;
44321         this.el.dom.value = state ? this.inputValue : this.valueOff;
44322         this.inSetChecked = false;
44323         
44324     },
44325     // handle setting of hidden value by some other method!!?!?
44326     setFromHidden: function()
44327     {
44328         if(!this.el){
44329             return;
44330         }
44331         //console.log("SET FROM HIDDEN");
44332         //alert('setFrom hidden');
44333         this.setValue(this.el.dom.value);
44334     },
44335     
44336     onDestroy : function()
44337     {
44338         if(this.viewEl){
44339             Roo.get(this.viewEl).remove();
44340         }
44341          
44342         Roo.form.Checkbox.superclass.onDestroy.call(this);
44343     },
44344     
44345     setBoxLabel : function(str)
44346     {
44347         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44348     }
44349
44350 });/*
44351  * Based on:
44352  * Ext JS Library 1.1.1
44353  * Copyright(c) 2006-2007, Ext JS, LLC.
44354  *
44355  * Originally Released Under LGPL - original licence link has changed is not relivant.
44356  *
44357  * Fork - LGPL
44358  * <script type="text/javascript">
44359  */
44360  
44361 /**
44362  * @class Roo.form.Radio
44363  * @extends Roo.form.Checkbox
44364  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44365  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44366  * @constructor
44367  * Creates a new Radio
44368  * @param {Object} config Configuration options
44369  */
44370 Roo.form.Radio = function(){
44371     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44372 };
44373 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44374     inputType: 'radio',
44375
44376     /**
44377      * If this radio is part of a group, it will return the selected value
44378      * @return {String}
44379      */
44380     getGroupValue : function(){
44381         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44382     },
44383     
44384     
44385     onRender : function(ct, position){
44386         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44387         
44388         if(this.inputValue !== undefined){
44389             this.el.dom.value = this.inputValue;
44390         }
44391          
44392         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44393         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44394         //var viewEl = this.wrap.createChild({ 
44395         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44396         //this.viewEl = viewEl;   
44397         //this.wrap.on('click', this.onClick,  this); 
44398         
44399         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44400         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44401         
44402         
44403         
44404         if(this.boxLabel){
44405             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44406         //    viewEl.on('click', this.onClick,  this); 
44407         }
44408          if(this.checked){
44409             this.el.dom.checked =   'checked' ;
44410         }
44411          
44412     } 
44413     
44414     
44415 });//<script type="text/javascript">
44416
44417 /*
44418  * Based  Ext JS Library 1.1.1
44419  * Copyright(c) 2006-2007, Ext JS, LLC.
44420  * LGPL
44421  *
44422  */
44423  
44424 /**
44425  * @class Roo.HtmlEditorCore
44426  * @extends Roo.Component
44427  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
44428  *
44429  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44430  */
44431
44432 Roo.HtmlEditorCore = function(config){
44433     
44434     
44435     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
44436     
44437     
44438     this.addEvents({
44439         /**
44440          * @event initialize
44441          * Fires when the editor is fully initialized (including the iframe)
44442          * @param {Roo.HtmlEditorCore} this
44443          */
44444         initialize: true,
44445         /**
44446          * @event activate
44447          * Fires when the editor is first receives the focus. Any insertion must wait
44448          * until after this event.
44449          * @param {Roo.HtmlEditorCore} this
44450          */
44451         activate: true,
44452          /**
44453          * @event beforesync
44454          * Fires before the textarea is updated with content from the editor iframe. Return false
44455          * to cancel the sync.
44456          * @param {Roo.HtmlEditorCore} this
44457          * @param {String} html
44458          */
44459         beforesync: true,
44460          /**
44461          * @event beforepush
44462          * Fires before the iframe editor is updated with content from the textarea. Return false
44463          * to cancel the push.
44464          * @param {Roo.HtmlEditorCore} this
44465          * @param {String} html
44466          */
44467         beforepush: true,
44468          /**
44469          * @event sync
44470          * Fires when the textarea is updated with content from the editor iframe.
44471          * @param {Roo.HtmlEditorCore} this
44472          * @param {String} html
44473          */
44474         sync: true,
44475          /**
44476          * @event push
44477          * Fires when the iframe editor is updated with content from the textarea.
44478          * @param {Roo.HtmlEditorCore} this
44479          * @param {String} html
44480          */
44481         push: true,
44482         
44483         /**
44484          * @event editorevent
44485          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44486          * @param {Roo.HtmlEditorCore} this
44487          */
44488         editorevent: true
44489         
44490     });
44491     
44492     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
44493     
44494     // defaults : white / black...
44495     this.applyBlacklists();
44496     
44497     
44498     
44499 };
44500
44501
44502 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
44503
44504
44505      /**
44506      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
44507      */
44508     
44509     owner : false,
44510     
44511      /**
44512      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44513      *                        Roo.resizable.
44514      */
44515     resizable : false,
44516      /**
44517      * @cfg {Number} height (in pixels)
44518      */   
44519     height: 300,
44520    /**
44521      * @cfg {Number} width (in pixels)
44522      */   
44523     width: 500,
44524     
44525     /**
44526      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44527      * 
44528      */
44529     stylesheets: false,
44530     
44531     /**
44532      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
44533      */
44534     allowComments: false,
44535     // id of frame..
44536     frameId: false,
44537     
44538     // private properties
44539     validationEvent : false,
44540     deferHeight: true,
44541     initialized : false,
44542     activated : false,
44543     sourceEditMode : false,
44544     onFocus : Roo.emptyFn,
44545     iframePad:3,
44546     hideMode:'offsets',
44547     
44548     clearUp: true,
44549     
44550     // blacklist + whitelisted elements..
44551     black: false,
44552     white: false,
44553      
44554     bodyCls : '',
44555
44556     /**
44557      * Protected method that will not generally be called directly. It
44558      * is called when the editor initializes the iframe with HTML contents. Override this method if you
44559      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
44560      */
44561     getDocMarkup : function(){
44562         // body styles..
44563         var st = '';
44564         
44565         // inherit styels from page...?? 
44566         if (this.stylesheets === false) {
44567             
44568             Roo.get(document.head).select('style').each(function(node) {
44569                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44570             });
44571             
44572             Roo.get(document.head).select('link').each(function(node) { 
44573                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44574             });
44575             
44576         } else if (!this.stylesheets.length) {
44577                 // simple..
44578                 st = '<style type="text/css">' +
44579                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44580                    '</style>';
44581         } else {
44582             for (var i in this.stylesheets) { 
44583                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
44584             }
44585             
44586         }
44587         
44588         st +=  '<style type="text/css">' +
44589             'IMG { cursor: pointer } ' +
44590         '</style>';
44591
44592         var cls = 'roo-htmleditor-body';
44593         
44594         if(this.bodyCls.length){
44595             cls += ' ' + this.bodyCls;
44596         }
44597         
44598         return '<html><head>' + st  +
44599             //<style type="text/css">' +
44600             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44601             //'</style>' +
44602             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
44603     },
44604
44605     // private
44606     onRender : function(ct, position)
44607     {
44608         var _t = this;
44609         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
44610         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
44611         
44612         
44613         this.el.dom.style.border = '0 none';
44614         this.el.dom.setAttribute('tabIndex', -1);
44615         this.el.addClass('x-hidden hide');
44616         
44617         
44618         
44619         if(Roo.isIE){ // fix IE 1px bogus margin
44620             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
44621         }
44622        
44623         
44624         this.frameId = Roo.id();
44625         
44626          
44627         
44628         var iframe = this.owner.wrap.createChild({
44629             tag: 'iframe',
44630             cls: 'form-control', // bootstrap..
44631             id: this.frameId,
44632             name: this.frameId,
44633             frameBorder : 'no',
44634             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
44635         }, this.el
44636         );
44637         
44638         
44639         this.iframe = iframe.dom;
44640
44641          this.assignDocWin();
44642         
44643         this.doc.designMode = 'on';
44644        
44645         this.doc.open();
44646         this.doc.write(this.getDocMarkup());
44647         this.doc.close();
44648
44649         
44650         var task = { // must defer to wait for browser to be ready
44651             run : function(){
44652                 //console.log("run task?" + this.doc.readyState);
44653                 this.assignDocWin();
44654                 if(this.doc.body || this.doc.readyState == 'complete'){
44655                     try {
44656                         this.doc.designMode="on";
44657                     } catch (e) {
44658                         return;
44659                     }
44660                     Roo.TaskMgr.stop(task);
44661                     this.initEditor.defer(10, this);
44662                 }
44663             },
44664             interval : 10,
44665             duration: 10000,
44666             scope: this
44667         };
44668         Roo.TaskMgr.start(task);
44669
44670     },
44671
44672     // private
44673     onResize : function(w, h)
44674     {
44675          Roo.log('resize: ' +w + ',' + h );
44676         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
44677         if(!this.iframe){
44678             return;
44679         }
44680         if(typeof w == 'number'){
44681             
44682             this.iframe.style.width = w + 'px';
44683         }
44684         if(typeof h == 'number'){
44685             
44686             this.iframe.style.height = h + 'px';
44687             if(this.doc){
44688                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
44689             }
44690         }
44691         
44692     },
44693
44694     /**
44695      * Toggles the editor between standard and source edit mode.
44696      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44697      */
44698     toggleSourceEdit : function(sourceEditMode){
44699         
44700         this.sourceEditMode = sourceEditMode === true;
44701         
44702         if(this.sourceEditMode){
44703  
44704             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
44705             
44706         }else{
44707             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
44708             //this.iframe.className = '';
44709             this.deferFocus();
44710         }
44711         //this.setSize(this.owner.wrap.getSize());
44712         //this.fireEvent('editmodechange', this, this.sourceEditMode);
44713     },
44714
44715     
44716   
44717
44718     /**
44719      * Protected method that will not generally be called directly. If you need/want
44720      * custom HTML cleanup, this is the method you should override.
44721      * @param {String} html The HTML to be cleaned
44722      * return {String} The cleaned HTML
44723      */
44724     cleanHtml : function(html){
44725         html = String(html);
44726         if(html.length > 5){
44727             if(Roo.isSafari){ // strip safari nonsense
44728                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44729             }
44730         }
44731         if(html == '&nbsp;'){
44732             html = '';
44733         }
44734         return html;
44735     },
44736
44737     /**
44738      * HTML Editor -> Textarea
44739      * Protected method that will not generally be called directly. Syncs the contents
44740      * of the editor iframe with the textarea.
44741      */
44742     syncValue : function(){
44743         if(this.initialized){
44744             var bd = (this.doc.body || this.doc.documentElement);
44745             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44746             var html = bd.innerHTML;
44747             if(Roo.isSafari){
44748                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44749                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44750                 if(m && m[1]){
44751                     html = '<div style="'+m[0]+'">' + html + '</div>';
44752                 }
44753             }
44754             html = this.cleanHtml(html);
44755             // fix up the special chars.. normaly like back quotes in word...
44756             // however we do not want to do this with chinese..
44757             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44758                 
44759                 var cc = match.charCodeAt();
44760
44761                 // Get the character value, handling surrogate pairs
44762                 if (match.length == 2) {
44763                     // It's a surrogate pair, calculate the Unicode code point
44764                     var high = match.charCodeAt(0) - 0xD800;
44765                     var low  = match.charCodeAt(1) - 0xDC00;
44766                     cc = (high * 0x400) + low + 0x10000;
44767                 }  else if (
44768                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44769                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44770                     (cc >= 0xf900 && cc < 0xfb00 )
44771                 ) {
44772                         return match;
44773                 }  
44774          
44775                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44776                 return "&#" + cc + ";";
44777                 
44778                 
44779             });
44780             
44781             
44782              
44783             if(this.owner.fireEvent('beforesync', this, html) !== false){
44784                 this.el.dom.value = html;
44785                 this.owner.fireEvent('sync', this, html);
44786             }
44787         }
44788     },
44789
44790     /**
44791      * Protected method that will not generally be called directly. Pushes the value of the textarea
44792      * into the iframe editor.
44793      */
44794     pushValue : function(){
44795         if(this.initialized){
44796             var v = this.el.dom.value.trim();
44797             
44798 //            if(v.length < 1){
44799 //                v = '&#160;';
44800 //            }
44801             
44802             if(this.owner.fireEvent('beforepush', this, v) !== false){
44803                 var d = (this.doc.body || this.doc.documentElement);
44804                 d.innerHTML = v;
44805                 this.cleanUpPaste();
44806                 this.el.dom.value = d.innerHTML;
44807                 this.owner.fireEvent('push', this, v);
44808             }
44809         }
44810     },
44811
44812     // private
44813     deferFocus : function(){
44814         this.focus.defer(10, this);
44815     },
44816
44817     // doc'ed in Field
44818     focus : function(){
44819         if(this.win && !this.sourceEditMode){
44820             this.win.focus();
44821         }else{
44822             this.el.focus();
44823         }
44824     },
44825     
44826     assignDocWin: function()
44827     {
44828         var iframe = this.iframe;
44829         
44830          if(Roo.isIE){
44831             this.doc = iframe.contentWindow.document;
44832             this.win = iframe.contentWindow;
44833         } else {
44834 //            if (!Roo.get(this.frameId)) {
44835 //                return;
44836 //            }
44837 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44838 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44839             
44840             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44841                 return;
44842             }
44843             
44844             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44845             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44846         }
44847     },
44848     
44849     // private
44850     initEditor : function(){
44851         //console.log("INIT EDITOR");
44852         this.assignDocWin();
44853         
44854         
44855         
44856         this.doc.designMode="on";
44857         this.doc.open();
44858         this.doc.write(this.getDocMarkup());
44859         this.doc.close();
44860         
44861         var dbody = (this.doc.body || this.doc.documentElement);
44862         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44863         // this copies styles from the containing element into thsi one..
44864         // not sure why we need all of this..
44865         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44866         
44867         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44868         //ss['background-attachment'] = 'fixed'; // w3c
44869         dbody.bgProperties = 'fixed'; // ie
44870         //Roo.DomHelper.applyStyles(dbody, ss);
44871         Roo.EventManager.on(this.doc, {
44872             //'mousedown': this.onEditorEvent,
44873             'mouseup': this.onEditorEvent,
44874             'dblclick': this.onEditorEvent,
44875             'click': this.onEditorEvent,
44876             'keyup': this.onEditorEvent,
44877             buffer:100,
44878             scope: this
44879         });
44880         if(Roo.isGecko){
44881             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44882         }
44883         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44884             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44885         }
44886         this.initialized = true;
44887
44888         this.owner.fireEvent('initialize', this);
44889         this.pushValue();
44890     },
44891
44892     // private
44893     onDestroy : function(){
44894         
44895         
44896         
44897         if(this.rendered){
44898             
44899             //for (var i =0; i < this.toolbars.length;i++) {
44900             //    // fixme - ask toolbars for heights?
44901             //    this.toolbars[i].onDestroy();
44902            // }
44903             
44904             //this.wrap.dom.innerHTML = '';
44905             //this.wrap.remove();
44906         }
44907     },
44908
44909     // private
44910     onFirstFocus : function(){
44911         
44912         this.assignDocWin();
44913         
44914         
44915         this.activated = true;
44916          
44917     
44918         if(Roo.isGecko){ // prevent silly gecko errors
44919             this.win.focus();
44920             var s = this.win.getSelection();
44921             if(!s.focusNode || s.focusNode.nodeType != 3){
44922                 var r = s.getRangeAt(0);
44923                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44924                 r.collapse(true);
44925                 this.deferFocus();
44926             }
44927             try{
44928                 this.execCmd('useCSS', true);
44929                 this.execCmd('styleWithCSS', false);
44930             }catch(e){}
44931         }
44932         this.owner.fireEvent('activate', this);
44933     },
44934
44935     // private
44936     adjustFont: function(btn){
44937         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44938         //if(Roo.isSafari){ // safari
44939         //    adjust *= 2;
44940        // }
44941         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44942         if(Roo.isSafari){ // safari
44943             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44944             v =  (v < 10) ? 10 : v;
44945             v =  (v > 48) ? 48 : v;
44946             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44947             
44948         }
44949         
44950         
44951         v = Math.max(1, v+adjust);
44952         
44953         this.execCmd('FontSize', v  );
44954     },
44955
44956     onEditorEvent : function(e)
44957     {
44958         this.owner.fireEvent('editorevent', this, e);
44959       //  this.updateToolbar();
44960         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44961     },
44962
44963     insertTag : function(tg)
44964     {
44965         // could be a bit smarter... -> wrap the current selected tRoo..
44966         if (tg.toLowerCase() == 'span' ||
44967             tg.toLowerCase() == 'code' ||
44968             tg.toLowerCase() == 'sup' ||
44969             tg.toLowerCase() == 'sub' 
44970             ) {
44971             
44972             range = this.createRange(this.getSelection());
44973             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44974             wrappingNode.appendChild(range.extractContents());
44975             range.insertNode(wrappingNode);
44976
44977             return;
44978             
44979             
44980             
44981         }
44982         this.execCmd("formatblock",   tg);
44983         
44984     },
44985     
44986     insertText : function(txt)
44987     {
44988         
44989         
44990         var range = this.createRange();
44991         range.deleteContents();
44992                //alert(Sender.getAttribute('label'));
44993                
44994         range.insertNode(this.doc.createTextNode(txt));
44995     } ,
44996     
44997      
44998
44999     /**
45000      * Executes a Midas editor command on the editor document and performs necessary focus and
45001      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
45002      * @param {String} cmd The Midas command
45003      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45004      */
45005     relayCmd : function(cmd, value){
45006         this.win.focus();
45007         this.execCmd(cmd, value);
45008         this.owner.fireEvent('editorevent', this);
45009         //this.updateToolbar();
45010         this.owner.deferFocus();
45011     },
45012
45013     /**
45014      * Executes a Midas editor command directly on the editor document.
45015      * For visual commands, you should use {@link #relayCmd} instead.
45016      * <b>This should only be called after the editor is initialized.</b>
45017      * @param {String} cmd The Midas command
45018      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45019      */
45020     execCmd : function(cmd, value){
45021         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45022         this.syncValue();
45023     },
45024  
45025  
45026    
45027     /**
45028      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45029      * to insert tRoo.
45030      * @param {String} text | dom node.. 
45031      */
45032     insertAtCursor : function(text)
45033     {
45034         
45035         if(!this.activated){
45036             return;
45037         }
45038         /*
45039         if(Roo.isIE){
45040             this.win.focus();
45041             var r = this.doc.selection.createRange();
45042             if(r){
45043                 r.collapse(true);
45044                 r.pasteHTML(text);
45045                 this.syncValue();
45046                 this.deferFocus();
45047             
45048             }
45049             return;
45050         }
45051         */
45052         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
45053             this.win.focus();
45054             
45055             
45056             // from jquery ui (MIT licenced)
45057             var range, node;
45058             var win = this.win;
45059             
45060             if (win.getSelection && win.getSelection().getRangeAt) {
45061                 range = win.getSelection().getRangeAt(0);
45062                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
45063                 range.insertNode(node);
45064             } else if (win.document.selection && win.document.selection.createRange) {
45065                 // no firefox support
45066                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45067                 win.document.selection.createRange().pasteHTML(txt);
45068             } else {
45069                 // no firefox support
45070                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45071                 this.execCmd('InsertHTML', txt);
45072             } 
45073             
45074             this.syncValue();
45075             
45076             this.deferFocus();
45077         }
45078     },
45079  // private
45080     mozKeyPress : function(e){
45081         if(e.ctrlKey){
45082             var c = e.getCharCode(), cmd;
45083           
45084             if(c > 0){
45085                 c = String.fromCharCode(c).toLowerCase();
45086                 switch(c){
45087                     case 'b':
45088                         cmd = 'bold';
45089                         break;
45090                     case 'i':
45091                         cmd = 'italic';
45092                         break;
45093                     
45094                     case 'u':
45095                         cmd = 'underline';
45096                         break;
45097                     
45098                     case 'v':
45099                         this.cleanUpPaste.defer(100, this);
45100                         return;
45101                         
45102                 }
45103                 if(cmd){
45104                     this.win.focus();
45105                     this.execCmd(cmd);
45106                     this.deferFocus();
45107                     e.preventDefault();
45108                 }
45109                 
45110             }
45111         }
45112     },
45113
45114     // private
45115     fixKeys : function(){ // load time branching for fastest keydown performance
45116         if(Roo.isIE){
45117             return function(e){
45118                 var k = e.getKey(), r;
45119                 if(k == e.TAB){
45120                     e.stopEvent();
45121                     r = this.doc.selection.createRange();
45122                     if(r){
45123                         r.collapse(true);
45124                         r.pasteHTML('&#160;&#160;&#160;&#160;');
45125                         this.deferFocus();
45126                     }
45127                     return;
45128                 }
45129                 
45130                 if(k == e.ENTER){
45131                     r = this.doc.selection.createRange();
45132                     if(r){
45133                         var target = r.parentElement();
45134                         if(!target || target.tagName.toLowerCase() != 'li'){
45135                             e.stopEvent();
45136                             r.pasteHTML('<br />');
45137                             r.collapse(false);
45138                             r.select();
45139                         }
45140                     }
45141                 }
45142                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45143                     this.cleanUpPaste.defer(100, this);
45144                     return;
45145                 }
45146                 
45147                 
45148             };
45149         }else if(Roo.isOpera){
45150             return function(e){
45151                 var k = e.getKey();
45152                 if(k == e.TAB){
45153                     e.stopEvent();
45154                     this.win.focus();
45155                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
45156                     this.deferFocus();
45157                 }
45158                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45159                     this.cleanUpPaste.defer(100, this);
45160                     return;
45161                 }
45162                 
45163             };
45164         }else if(Roo.isSafari){
45165             return function(e){
45166                 var k = e.getKey();
45167                 
45168                 if(k == e.TAB){
45169                     e.stopEvent();
45170                     this.execCmd('InsertText','\t');
45171                     this.deferFocus();
45172                     return;
45173                 }
45174                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45175                     this.cleanUpPaste.defer(100, this);
45176                     return;
45177                 }
45178                 
45179              };
45180         }
45181     }(),
45182     
45183     getAllAncestors: function()
45184     {
45185         var p = this.getSelectedNode();
45186         var a = [];
45187         if (!p) {
45188             a.push(p); // push blank onto stack..
45189             p = this.getParentElement();
45190         }
45191         
45192         
45193         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
45194             a.push(p);
45195             p = p.parentNode;
45196         }
45197         a.push(this.doc.body);
45198         return a;
45199     },
45200     lastSel : false,
45201     lastSelNode : false,
45202     
45203     
45204     getSelection : function() 
45205     {
45206         this.assignDocWin();
45207         return Roo.isIE ? this.doc.selection : this.win.getSelection();
45208     },
45209     
45210     getSelectedNode: function() 
45211     {
45212         // this may only work on Gecko!!!
45213         
45214         // should we cache this!!!!
45215         
45216         
45217         
45218          
45219         var range = this.createRange(this.getSelection()).cloneRange();
45220         
45221         if (Roo.isIE) {
45222             var parent = range.parentElement();
45223             while (true) {
45224                 var testRange = range.duplicate();
45225                 testRange.moveToElementText(parent);
45226                 if (testRange.inRange(range)) {
45227                     break;
45228                 }
45229                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
45230                     break;
45231                 }
45232                 parent = parent.parentElement;
45233             }
45234             return parent;
45235         }
45236         
45237         // is ancestor a text element.
45238         var ac =  range.commonAncestorContainer;
45239         if (ac.nodeType == 3) {
45240             ac = ac.parentNode;
45241         }
45242         
45243         var ar = ac.childNodes;
45244          
45245         var nodes = [];
45246         var other_nodes = [];
45247         var has_other_nodes = false;
45248         for (var i=0;i<ar.length;i++) {
45249             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
45250                 continue;
45251             }
45252             // fullly contained node.
45253             
45254             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
45255                 nodes.push(ar[i]);
45256                 continue;
45257             }
45258             
45259             // probably selected..
45260             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
45261                 other_nodes.push(ar[i]);
45262                 continue;
45263             }
45264             // outer..
45265             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
45266                 continue;
45267             }
45268             
45269             
45270             has_other_nodes = true;
45271         }
45272         if (!nodes.length && other_nodes.length) {
45273             nodes= other_nodes;
45274         }
45275         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
45276             return false;
45277         }
45278         
45279         return nodes[0];
45280     },
45281     createRange: function(sel)
45282     {
45283         // this has strange effects when using with 
45284         // top toolbar - not sure if it's a great idea.
45285         //this.editor.contentWindow.focus();
45286         if (typeof sel != "undefined") {
45287             try {
45288                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
45289             } catch(e) {
45290                 return this.doc.createRange();
45291             }
45292         } else {
45293             return this.doc.createRange();
45294         }
45295     },
45296     getParentElement: function()
45297     {
45298         
45299         this.assignDocWin();
45300         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
45301         
45302         var range = this.createRange(sel);
45303          
45304         try {
45305             var p = range.commonAncestorContainer;
45306             while (p.nodeType == 3) { // text node
45307                 p = p.parentNode;
45308             }
45309             return p;
45310         } catch (e) {
45311             return null;
45312         }
45313     
45314     },
45315     /***
45316      *
45317      * Range intersection.. the hard stuff...
45318      *  '-1' = before
45319      *  '0' = hits..
45320      *  '1' = after.
45321      *         [ -- selected range --- ]
45322      *   [fail]                        [fail]
45323      *
45324      *    basically..
45325      *      if end is before start or  hits it. fail.
45326      *      if start is after end or hits it fail.
45327      *
45328      *   if either hits (but other is outside. - then it's not 
45329      *   
45330      *    
45331      **/
45332     
45333     
45334     // @see http://www.thismuchiknow.co.uk/?p=64.
45335     rangeIntersectsNode : function(range, node)
45336     {
45337         var nodeRange = node.ownerDocument.createRange();
45338         try {
45339             nodeRange.selectNode(node);
45340         } catch (e) {
45341             nodeRange.selectNodeContents(node);
45342         }
45343     
45344         var rangeStartRange = range.cloneRange();
45345         rangeStartRange.collapse(true);
45346     
45347         var rangeEndRange = range.cloneRange();
45348         rangeEndRange.collapse(false);
45349     
45350         var nodeStartRange = nodeRange.cloneRange();
45351         nodeStartRange.collapse(true);
45352     
45353         var nodeEndRange = nodeRange.cloneRange();
45354         nodeEndRange.collapse(false);
45355     
45356         return rangeStartRange.compareBoundaryPoints(
45357                  Range.START_TO_START, nodeEndRange) == -1 &&
45358                rangeEndRange.compareBoundaryPoints(
45359                  Range.START_TO_START, nodeStartRange) == 1;
45360         
45361          
45362     },
45363     rangeCompareNode : function(range, node)
45364     {
45365         var nodeRange = node.ownerDocument.createRange();
45366         try {
45367             nodeRange.selectNode(node);
45368         } catch (e) {
45369             nodeRange.selectNodeContents(node);
45370         }
45371         
45372         
45373         range.collapse(true);
45374     
45375         nodeRange.collapse(true);
45376      
45377         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
45378         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
45379          
45380         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
45381         
45382         var nodeIsBefore   =  ss == 1;
45383         var nodeIsAfter    = ee == -1;
45384         
45385         if (nodeIsBefore && nodeIsAfter) {
45386             return 0; // outer
45387         }
45388         if (!nodeIsBefore && nodeIsAfter) {
45389             return 1; //right trailed.
45390         }
45391         
45392         if (nodeIsBefore && !nodeIsAfter) {
45393             return 2;  // left trailed.
45394         }
45395         // fully contined.
45396         return 3;
45397     },
45398
45399     // private? - in a new class?
45400     cleanUpPaste :  function()
45401     {
45402         // cleans up the whole document..
45403         Roo.log('cleanuppaste');
45404         
45405         this.cleanUpChildren(this.doc.body);
45406         var clean = this.cleanWordChars(this.doc.body.innerHTML);
45407         if (clean != this.doc.body.innerHTML) {
45408             this.doc.body.innerHTML = clean;
45409         }
45410         
45411     },
45412     
45413     cleanWordChars : function(input) {// change the chars to hex code
45414         var he = Roo.HtmlEditorCore;
45415         
45416         var output = input;
45417         Roo.each(he.swapCodes, function(sw) { 
45418             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
45419             
45420             output = output.replace(swapper, sw[1]);
45421         });
45422         
45423         return output;
45424     },
45425     
45426     
45427     cleanUpChildren : function (n)
45428     {
45429         if (!n.childNodes.length) {
45430             return;
45431         }
45432         for (var i = n.childNodes.length-1; i > -1 ; i--) {
45433            this.cleanUpChild(n.childNodes[i]);
45434         }
45435     },
45436     
45437     
45438         
45439     
45440     cleanUpChild : function (node)
45441     {
45442         var ed = this;
45443         //console.log(node);
45444         if (node.nodeName == "#text") {
45445             // clean up silly Windows -- stuff?
45446             return; 
45447         }
45448         if (node.nodeName == "#comment") {
45449             if (!this.allowComments) {
45450                 node.parentNode.removeChild(node);
45451             }
45452             // clean up silly Windows -- stuff?
45453             return; 
45454         }
45455         var lcname = node.tagName.toLowerCase();
45456         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
45457         // whitelist of tags..
45458         
45459         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
45460             // remove node.
45461             node.parentNode.removeChild(node);
45462             return;
45463             
45464         }
45465         
45466         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
45467         
45468         // spans with no attributes - just remove them..
45469         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
45470             remove_keep_children = true;
45471         }
45472         
45473         // remove <a name=....> as rendering on yahoo mailer is borked with this.
45474         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
45475         
45476         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
45477         //    remove_keep_children = true;
45478         //}
45479         
45480         if (remove_keep_children) {
45481             this.cleanUpChildren(node);
45482             // inserts everything just before this node...
45483             while (node.childNodes.length) {
45484                 var cn = node.childNodes[0];
45485                 node.removeChild(cn);
45486                 node.parentNode.insertBefore(cn, node);
45487             }
45488             node.parentNode.removeChild(node);
45489             return;
45490         }
45491         
45492         if (!node.attributes || !node.attributes.length) {
45493             
45494           
45495             
45496             
45497             this.cleanUpChildren(node);
45498             return;
45499         }
45500         
45501         function cleanAttr(n,v)
45502         {
45503             
45504             if (v.match(/^\./) || v.match(/^\//)) {
45505                 return;
45506             }
45507             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
45508                 return;
45509             }
45510             if (v.match(/^#/)) {
45511                 return;
45512             }
45513             if (v.match(/^\{/)) { // allow template editing.
45514                 return;
45515             }
45516 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45517             node.removeAttribute(n);
45518             
45519         }
45520         
45521         var cwhite = this.cwhite;
45522         var cblack = this.cblack;
45523             
45524         function cleanStyle(n,v)
45525         {
45526             if (v.match(/expression/)) { //XSS?? should we even bother..
45527                 node.removeAttribute(n);
45528                 return;
45529             }
45530             
45531             var parts = v.split(/;/);
45532             var clean = [];
45533             
45534             Roo.each(parts, function(p) {
45535                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45536                 if (!p.length) {
45537                     return true;
45538                 }
45539                 var l = p.split(':').shift().replace(/\s+/g,'');
45540                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45541                 
45542                 if ( cwhite.length && cblack.indexOf(l) > -1) {
45543 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
45544                     //node.removeAttribute(n);
45545                     return true;
45546                 }
45547                 //Roo.log()
45548                 // only allow 'c whitelisted system attributes'
45549                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
45550 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
45551                     //node.removeAttribute(n);
45552                     return true;
45553                 }
45554                 
45555                 
45556                  
45557                 
45558                 clean.push(p);
45559                 return true;
45560             });
45561             if (clean.length) { 
45562                 node.setAttribute(n, clean.join(';'));
45563             } else {
45564                 node.removeAttribute(n);
45565             }
45566             
45567         }
45568         
45569         
45570         for (var i = node.attributes.length-1; i > -1 ; i--) {
45571             var a = node.attributes[i];
45572             //console.log(a);
45573             
45574             if (a.name.toLowerCase().substr(0,2)=='on')  {
45575                 node.removeAttribute(a.name);
45576                 continue;
45577             }
45578             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
45579                 node.removeAttribute(a.name);
45580                 continue;
45581             }
45582             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
45583                 cleanAttr(a.name,a.value); // fixme..
45584                 continue;
45585             }
45586             if (a.name == 'style') {
45587                 cleanStyle(a.name,a.value);
45588                 continue;
45589             }
45590             /// clean up MS crap..
45591             // tecnically this should be a list of valid class'es..
45592             
45593             
45594             if (a.name == 'class') {
45595                 if (a.value.match(/^Mso/)) {
45596                     node.removeAttribute('class');
45597                 }
45598                 
45599                 if (a.value.match(/^body$/)) {
45600                     node.removeAttribute('class');
45601                 }
45602                 continue;
45603             }
45604             
45605             // style cleanup!?
45606             // class cleanup?
45607             
45608         }
45609         
45610         
45611         this.cleanUpChildren(node);
45612         
45613         
45614     },
45615     
45616     /**
45617      * Clean up MS wordisms...
45618      */
45619     cleanWord : function(node)
45620     {
45621         if (!node) {
45622             this.cleanWord(this.doc.body);
45623             return;
45624         }
45625         
45626         if(
45627                 node.nodeName == 'SPAN' &&
45628                 !node.hasAttributes() &&
45629                 node.childNodes.length == 1 &&
45630                 node.firstChild.nodeName == "#text"  
45631         ) {
45632             var textNode = node.firstChild;
45633             node.removeChild(textNode);
45634             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45635                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45636             }
45637             node.parentNode.insertBefore(textNode, node);
45638             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45639                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45640             }
45641             node.parentNode.removeChild(node);
45642         }
45643         
45644         if (node.nodeName == "#text") {
45645             // clean up silly Windows -- stuff?
45646             return; 
45647         }
45648         if (node.nodeName == "#comment") {
45649             node.parentNode.removeChild(node);
45650             // clean up silly Windows -- stuff?
45651             return; 
45652         }
45653         
45654         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45655             node.parentNode.removeChild(node);
45656             return;
45657         }
45658         //Roo.log(node.tagName);
45659         // remove - but keep children..
45660         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45661             //Roo.log('-- removed');
45662             while (node.childNodes.length) {
45663                 var cn = node.childNodes[0];
45664                 node.removeChild(cn);
45665                 node.parentNode.insertBefore(cn, node);
45666                 // move node to parent - and clean it..
45667                 this.cleanWord(cn);
45668             }
45669             node.parentNode.removeChild(node);
45670             /// no need to iterate chidlren = it's got none..
45671             //this.iterateChildren(node, this.cleanWord);
45672             return;
45673         }
45674         // clean styles
45675         if (node.className.length) {
45676             
45677             var cn = node.className.split(/\W+/);
45678             var cna = [];
45679             Roo.each(cn, function(cls) {
45680                 if (cls.match(/Mso[a-zA-Z]+/)) {
45681                     return;
45682                 }
45683                 cna.push(cls);
45684             });
45685             node.className = cna.length ? cna.join(' ') : '';
45686             if (!cna.length) {
45687                 node.removeAttribute("class");
45688             }
45689         }
45690         
45691         if (node.hasAttribute("lang")) {
45692             node.removeAttribute("lang");
45693         }
45694         
45695         if (node.hasAttribute("style")) {
45696             
45697             var styles = node.getAttribute("style").split(";");
45698             var nstyle = [];
45699             Roo.each(styles, function(s) {
45700                 if (!s.match(/:/)) {
45701                     return;
45702                 }
45703                 var kv = s.split(":");
45704                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45705                     return;
45706                 }
45707                 // what ever is left... we allow.
45708                 nstyle.push(s);
45709             });
45710             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45711             if (!nstyle.length) {
45712                 node.removeAttribute('style');
45713             }
45714         }
45715         this.iterateChildren(node, this.cleanWord);
45716         
45717         
45718         
45719     },
45720     /**
45721      * iterateChildren of a Node, calling fn each time, using this as the scole..
45722      * @param {DomNode} node node to iterate children of.
45723      * @param {Function} fn method of this class to call on each item.
45724      */
45725     iterateChildren : function(node, fn)
45726     {
45727         if (!node.childNodes.length) {
45728                 return;
45729         }
45730         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45731            fn.call(this, node.childNodes[i])
45732         }
45733     },
45734     
45735     
45736     /**
45737      * cleanTableWidths.
45738      *
45739      * Quite often pasting from word etc.. results in tables with column and widths.
45740      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45741      *
45742      */
45743     cleanTableWidths : function(node)
45744     {
45745          
45746          
45747         if (!node) {
45748             this.cleanTableWidths(this.doc.body);
45749             return;
45750         }
45751         
45752         // ignore list...
45753         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45754             return; 
45755         }
45756         Roo.log(node.tagName);
45757         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45758             this.iterateChildren(node, this.cleanTableWidths);
45759             return;
45760         }
45761         if (node.hasAttribute('width')) {
45762             node.removeAttribute('width');
45763         }
45764         
45765          
45766         if (node.hasAttribute("style")) {
45767             // pretty basic...
45768             
45769             var styles = node.getAttribute("style").split(";");
45770             var nstyle = [];
45771             Roo.each(styles, function(s) {
45772                 if (!s.match(/:/)) {
45773                     return;
45774                 }
45775                 var kv = s.split(":");
45776                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45777                     return;
45778                 }
45779                 // what ever is left... we allow.
45780                 nstyle.push(s);
45781             });
45782             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45783             if (!nstyle.length) {
45784                 node.removeAttribute('style');
45785             }
45786         }
45787         
45788         this.iterateChildren(node, this.cleanTableWidths);
45789         
45790         
45791     },
45792     
45793     
45794     
45795     
45796     domToHTML : function(currentElement, depth, nopadtext) {
45797         
45798         depth = depth || 0;
45799         nopadtext = nopadtext || false;
45800     
45801         if (!currentElement) {
45802             return this.domToHTML(this.doc.body);
45803         }
45804         
45805         //Roo.log(currentElement);
45806         var j;
45807         var allText = false;
45808         var nodeName = currentElement.nodeName;
45809         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45810         
45811         if  (nodeName == '#text') {
45812             
45813             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45814         }
45815         
45816         
45817         var ret = '';
45818         if (nodeName != 'BODY') {
45819              
45820             var i = 0;
45821             // Prints the node tagName, such as <A>, <IMG>, etc
45822             if (tagName) {
45823                 var attr = [];
45824                 for(i = 0; i < currentElement.attributes.length;i++) {
45825                     // quoting?
45826                     var aname = currentElement.attributes.item(i).name;
45827                     if (!currentElement.attributes.item(i).value.length) {
45828                         continue;
45829                     }
45830                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45831                 }
45832                 
45833                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45834             } 
45835             else {
45836                 
45837                 // eack
45838             }
45839         } else {
45840             tagName = false;
45841         }
45842         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45843             return ret;
45844         }
45845         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45846             nopadtext = true;
45847         }
45848         
45849         
45850         // Traverse the tree
45851         i = 0;
45852         var currentElementChild = currentElement.childNodes.item(i);
45853         var allText = true;
45854         var innerHTML  = '';
45855         lastnode = '';
45856         while (currentElementChild) {
45857             // Formatting code (indent the tree so it looks nice on the screen)
45858             var nopad = nopadtext;
45859             if (lastnode == 'SPAN') {
45860                 nopad  = true;
45861             }
45862             // text
45863             if  (currentElementChild.nodeName == '#text') {
45864                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45865                 toadd = nopadtext ? toadd : toadd.trim();
45866                 if (!nopad && toadd.length > 80) {
45867                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45868                 }
45869                 innerHTML  += toadd;
45870                 
45871                 i++;
45872                 currentElementChild = currentElement.childNodes.item(i);
45873                 lastNode = '';
45874                 continue;
45875             }
45876             allText = false;
45877             
45878             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45879                 
45880             // Recursively traverse the tree structure of the child node
45881             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45882             lastnode = currentElementChild.nodeName;
45883             i++;
45884             currentElementChild=currentElement.childNodes.item(i);
45885         }
45886         
45887         ret += innerHTML;
45888         
45889         if (!allText) {
45890                 // The remaining code is mostly for formatting the tree
45891             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45892         }
45893         
45894         
45895         if (tagName) {
45896             ret+= "</"+tagName+">";
45897         }
45898         return ret;
45899         
45900     },
45901         
45902     applyBlacklists : function()
45903     {
45904         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45905         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45906         
45907         this.white = [];
45908         this.black = [];
45909         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45910             if (b.indexOf(tag) > -1) {
45911                 return;
45912             }
45913             this.white.push(tag);
45914             
45915         }, this);
45916         
45917         Roo.each(w, function(tag) {
45918             if (b.indexOf(tag) > -1) {
45919                 return;
45920             }
45921             if (this.white.indexOf(tag) > -1) {
45922                 return;
45923             }
45924             this.white.push(tag);
45925             
45926         }, this);
45927         
45928         
45929         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45930             if (w.indexOf(tag) > -1) {
45931                 return;
45932             }
45933             this.black.push(tag);
45934             
45935         }, this);
45936         
45937         Roo.each(b, function(tag) {
45938             if (w.indexOf(tag) > -1) {
45939                 return;
45940             }
45941             if (this.black.indexOf(tag) > -1) {
45942                 return;
45943             }
45944             this.black.push(tag);
45945             
45946         }, this);
45947         
45948         
45949         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45950         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45951         
45952         this.cwhite = [];
45953         this.cblack = [];
45954         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45955             if (b.indexOf(tag) > -1) {
45956                 return;
45957             }
45958             this.cwhite.push(tag);
45959             
45960         }, this);
45961         
45962         Roo.each(w, function(tag) {
45963             if (b.indexOf(tag) > -1) {
45964                 return;
45965             }
45966             if (this.cwhite.indexOf(tag) > -1) {
45967                 return;
45968             }
45969             this.cwhite.push(tag);
45970             
45971         }, this);
45972         
45973         
45974         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45975             if (w.indexOf(tag) > -1) {
45976                 return;
45977             }
45978             this.cblack.push(tag);
45979             
45980         }, this);
45981         
45982         Roo.each(b, function(tag) {
45983             if (w.indexOf(tag) > -1) {
45984                 return;
45985             }
45986             if (this.cblack.indexOf(tag) > -1) {
45987                 return;
45988             }
45989             this.cblack.push(tag);
45990             
45991         }, this);
45992     },
45993     
45994     setStylesheets : function(stylesheets)
45995     {
45996         if(typeof(stylesheets) == 'string'){
45997             Roo.get(this.iframe.contentDocument.head).createChild({
45998                 tag : 'link',
45999                 rel : 'stylesheet',
46000                 type : 'text/css',
46001                 href : stylesheets
46002             });
46003             
46004             return;
46005         }
46006         var _this = this;
46007      
46008         Roo.each(stylesheets, function(s) {
46009             if(!s.length){
46010                 return;
46011             }
46012             
46013             Roo.get(_this.iframe.contentDocument.head).createChild({
46014                 tag : 'link',
46015                 rel : 'stylesheet',
46016                 type : 'text/css',
46017                 href : s
46018             });
46019         });
46020
46021         
46022     },
46023     
46024     removeStylesheets : function()
46025     {
46026         var _this = this;
46027         
46028         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46029             s.remove();
46030         });
46031     },
46032     
46033     setStyle : function(style)
46034     {
46035         Roo.get(this.iframe.contentDocument.head).createChild({
46036             tag : 'style',
46037             type : 'text/css',
46038             html : style
46039         });
46040
46041         return;
46042     }
46043     
46044     // hide stuff that is not compatible
46045     /**
46046      * @event blur
46047      * @hide
46048      */
46049     /**
46050      * @event change
46051      * @hide
46052      */
46053     /**
46054      * @event focus
46055      * @hide
46056      */
46057     /**
46058      * @event specialkey
46059      * @hide
46060      */
46061     /**
46062      * @cfg {String} fieldClass @hide
46063      */
46064     /**
46065      * @cfg {String} focusClass @hide
46066      */
46067     /**
46068      * @cfg {String} autoCreate @hide
46069      */
46070     /**
46071      * @cfg {String} inputType @hide
46072      */
46073     /**
46074      * @cfg {String} invalidClass @hide
46075      */
46076     /**
46077      * @cfg {String} invalidText @hide
46078      */
46079     /**
46080      * @cfg {String} msgFx @hide
46081      */
46082     /**
46083      * @cfg {String} validateOnBlur @hide
46084      */
46085 });
46086
46087 Roo.HtmlEditorCore.white = [
46088         'area', 'br', 'img', 'input', 'hr', 'wbr',
46089         
46090        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46091        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46092        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46093        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46094        'table',   'ul',         'xmp', 
46095        
46096        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46097       'thead',   'tr', 
46098      
46099       'dir', 'menu', 'ol', 'ul', 'dl',
46100        
46101       'embed',  'object'
46102 ];
46103
46104
46105 Roo.HtmlEditorCore.black = [
46106     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46107         'applet', // 
46108         'base',   'basefont', 'bgsound', 'blink',  'body', 
46109         'frame',  'frameset', 'head',    'html',   'ilayer', 
46110         'iframe', 'layer',  'link',     'meta',    'object',   
46111         'script', 'style' ,'title',  'xml' // clean later..
46112 ];
46113 Roo.HtmlEditorCore.clean = [
46114     'script', 'style', 'title', 'xml'
46115 ];
46116 Roo.HtmlEditorCore.remove = [
46117     'font'
46118 ];
46119 // attributes..
46120
46121 Roo.HtmlEditorCore.ablack = [
46122     'on'
46123 ];
46124     
46125 Roo.HtmlEditorCore.aclean = [ 
46126     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46127 ];
46128
46129 // protocols..
46130 Roo.HtmlEditorCore.pwhite= [
46131         'http',  'https',  'mailto'
46132 ];
46133
46134 // white listed style attributes.
46135 Roo.HtmlEditorCore.cwhite= [
46136       //  'text-align', /// default is to allow most things..
46137       
46138          
46139 //        'font-size'//??
46140 ];
46141
46142 // black listed style attributes.
46143 Roo.HtmlEditorCore.cblack= [
46144       //  'font-size' -- this can be set by the project 
46145 ];
46146
46147
46148 Roo.HtmlEditorCore.swapCodes   =[ 
46149     [    8211, "&#8211;" ], 
46150     [    8212, "&#8212;" ], 
46151     [    8216,  "'" ],  
46152     [    8217, "'" ],  
46153     [    8220, '"' ],  
46154     [    8221, '"' ],  
46155     [    8226, "*" ],  
46156     [    8230, "..." ]
46157 ]; 
46158
46159     //<script type="text/javascript">
46160
46161 /*
46162  * Ext JS Library 1.1.1
46163  * Copyright(c) 2006-2007, Ext JS, LLC.
46164  * Licence LGPL
46165  * 
46166  */
46167  
46168  
46169 Roo.form.HtmlEditor = function(config){
46170     
46171     
46172     
46173     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46174     
46175     if (!this.toolbars) {
46176         this.toolbars = [];
46177     }
46178     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46179     
46180     
46181 };
46182
46183 /**
46184  * @class Roo.form.HtmlEditor
46185  * @extends Roo.form.Field
46186  * Provides a lightweight HTML Editor component.
46187  *
46188  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46189  * 
46190  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46191  * supported by this editor.</b><br/><br/>
46192  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46193  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46194  */
46195 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46196     /**
46197      * @cfg {Boolean} clearUp
46198      */
46199     clearUp : true,
46200       /**
46201      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46202      */
46203     toolbars : false,
46204    
46205      /**
46206      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46207      *                        Roo.resizable.
46208      */
46209     resizable : false,
46210      /**
46211      * @cfg {Number} height (in pixels)
46212      */   
46213     height: 300,
46214    /**
46215      * @cfg {Number} width (in pixels)
46216      */   
46217     width: 500,
46218     
46219     /**
46220      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46221      * 
46222      */
46223     stylesheets: false,
46224     
46225     
46226      /**
46227      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46228      * 
46229      */
46230     cblack: false,
46231     /**
46232      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46233      * 
46234      */
46235     cwhite: false,
46236     
46237      /**
46238      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46239      * 
46240      */
46241     black: false,
46242     /**
46243      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46244      * 
46245      */
46246     white: false,
46247     /**
46248      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46249      */
46250     allowComments: false,
46251     
46252     // id of frame..
46253     frameId: false,
46254     
46255     // private properties
46256     validationEvent : false,
46257     deferHeight: true,
46258     initialized : false,
46259     activated : false,
46260     
46261     onFocus : Roo.emptyFn,
46262     iframePad:3,
46263     hideMode:'offsets',
46264     
46265     actionMode : 'container', // defaults to hiding it...
46266     
46267     defaultAutoCreate : { // modified by initCompnoent..
46268         tag: "textarea",
46269         style:"width:500px;height:300px;",
46270         autocomplete: "new-password"
46271     },
46272
46273     // private
46274     initComponent : function(){
46275         this.addEvents({
46276             /**
46277              * @event initialize
46278              * Fires when the editor is fully initialized (including the iframe)
46279              * @param {HtmlEditor} this
46280              */
46281             initialize: true,
46282             /**
46283              * @event activate
46284              * Fires when the editor is first receives the focus. Any insertion must wait
46285              * until after this event.
46286              * @param {HtmlEditor} this
46287              */
46288             activate: true,
46289              /**
46290              * @event beforesync
46291              * Fires before the textarea is updated with content from the editor iframe. Return false
46292              * to cancel the sync.
46293              * @param {HtmlEditor} this
46294              * @param {String} html
46295              */
46296             beforesync: true,
46297              /**
46298              * @event beforepush
46299              * Fires before the iframe editor is updated with content from the textarea. Return false
46300              * to cancel the push.
46301              * @param {HtmlEditor} this
46302              * @param {String} html
46303              */
46304             beforepush: true,
46305              /**
46306              * @event sync
46307              * Fires when the textarea is updated with content from the editor iframe.
46308              * @param {HtmlEditor} this
46309              * @param {String} html
46310              */
46311             sync: true,
46312              /**
46313              * @event push
46314              * Fires when the iframe editor is updated with content from the textarea.
46315              * @param {HtmlEditor} this
46316              * @param {String} html
46317              */
46318             push: true,
46319              /**
46320              * @event editmodechange
46321              * Fires when the editor switches edit modes
46322              * @param {HtmlEditor} this
46323              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46324              */
46325             editmodechange: true,
46326             /**
46327              * @event editorevent
46328              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46329              * @param {HtmlEditor} this
46330              */
46331             editorevent: true,
46332             /**
46333              * @event firstfocus
46334              * Fires when on first focus - needed by toolbars..
46335              * @param {HtmlEditor} this
46336              */
46337             firstfocus: true,
46338             /**
46339              * @event autosave
46340              * Auto save the htmlEditor value as a file into Events
46341              * @param {HtmlEditor} this
46342              */
46343             autosave: true,
46344             /**
46345              * @event savedpreview
46346              * preview the saved version of htmlEditor
46347              * @param {HtmlEditor} this
46348              */
46349             savedpreview: true,
46350             
46351             /**
46352             * @event stylesheetsclick
46353             * Fires when press the Sytlesheets button
46354             * @param {Roo.HtmlEditorCore} this
46355             */
46356             stylesheetsclick: true
46357         });
46358         this.defaultAutoCreate =  {
46359             tag: "textarea",
46360             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46361             autocomplete: "new-password"
46362         };
46363     },
46364
46365     /**
46366      * Protected method that will not generally be called directly. It
46367      * is called when the editor creates its toolbar. Override this method if you need to
46368      * add custom toolbar buttons.
46369      * @param {HtmlEditor} editor
46370      */
46371     createToolbar : function(editor){
46372         Roo.log("create toolbars");
46373         if (!editor.toolbars || !editor.toolbars.length) {
46374             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46375         }
46376         
46377         for (var i =0 ; i < editor.toolbars.length;i++) {
46378             editor.toolbars[i] = Roo.factory(
46379                     typeof(editor.toolbars[i]) == 'string' ?
46380                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46381                 Roo.form.HtmlEditor);
46382             editor.toolbars[i].init(editor);
46383         }
46384          
46385         
46386     },
46387
46388      
46389     // private
46390     onRender : function(ct, position)
46391     {
46392         var _t = this;
46393         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46394         
46395         this.wrap = this.el.wrap({
46396             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46397         });
46398         
46399         this.editorcore.onRender(ct, position);
46400          
46401         if (this.resizable) {
46402             this.resizeEl = new Roo.Resizable(this.wrap, {
46403                 pinned : true,
46404                 wrap: true,
46405                 dynamic : true,
46406                 minHeight : this.height,
46407                 height: this.height,
46408                 handles : this.resizable,
46409                 width: this.width,
46410                 listeners : {
46411                     resize : function(r, w, h) {
46412                         _t.onResize(w,h); // -something
46413                     }
46414                 }
46415             });
46416             
46417         }
46418         this.createToolbar(this);
46419        
46420         
46421         if(!this.width){
46422             this.setSize(this.wrap.getSize());
46423         }
46424         if (this.resizeEl) {
46425             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46426             // should trigger onReize..
46427         }
46428         
46429         this.keyNav = new Roo.KeyNav(this.el, {
46430             
46431             "tab" : function(e){
46432                 e.preventDefault();
46433                 
46434                 var value = this.getValue();
46435                 
46436                 var start = this.el.dom.selectionStart;
46437                 var end = this.el.dom.selectionEnd;
46438                 
46439                 if(!e.shiftKey){
46440                     
46441                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46442                     this.el.dom.setSelectionRange(end + 1, end + 1);
46443                     return;
46444                 }
46445                 
46446                 var f = value.substring(0, start).split("\t");
46447                 
46448                 if(f.pop().length != 0){
46449                     return;
46450                 }
46451                 
46452                 this.setValue(f.join("\t") + value.substring(end));
46453                 this.el.dom.setSelectionRange(start - 1, start - 1);
46454                 
46455             },
46456             
46457             "home" : function(e){
46458                 e.preventDefault();
46459                 
46460                 var curr = this.el.dom.selectionStart;
46461                 var lines = this.getValue().split("\n");
46462                 
46463                 if(!lines.length){
46464                     return;
46465                 }
46466                 
46467                 if(e.ctrlKey){
46468                     this.el.dom.setSelectionRange(0, 0);
46469                     return;
46470                 }
46471                 
46472                 var pos = 0;
46473                 
46474                 for (var i = 0; i < lines.length;i++) {
46475                     pos += lines[i].length;
46476                     
46477                     if(i != 0){
46478                         pos += 1;
46479                     }
46480                     
46481                     if(pos < curr){
46482                         continue;
46483                     }
46484                     
46485                     pos -= lines[i].length;
46486                     
46487                     break;
46488                 }
46489                 
46490                 if(!e.shiftKey){
46491                     this.el.dom.setSelectionRange(pos, pos);
46492                     return;
46493                 }
46494                 
46495                 this.el.dom.selectionStart = pos;
46496                 this.el.dom.selectionEnd = curr;
46497             },
46498             
46499             "end" : function(e){
46500                 e.preventDefault();
46501                 
46502                 var curr = this.el.dom.selectionStart;
46503                 var lines = this.getValue().split("\n");
46504                 
46505                 if(!lines.length){
46506                     return;
46507                 }
46508                 
46509                 if(e.ctrlKey){
46510                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
46511                     return;
46512                 }
46513                 
46514                 var pos = 0;
46515                 
46516                 for (var i = 0; i < lines.length;i++) {
46517                     
46518                     pos += lines[i].length;
46519                     
46520                     if(i != 0){
46521                         pos += 1;
46522                     }
46523                     
46524                     if(pos < curr){
46525                         continue;
46526                     }
46527                     
46528                     break;
46529                 }
46530                 
46531                 if(!e.shiftKey){
46532                     this.el.dom.setSelectionRange(pos, pos);
46533                     return;
46534                 }
46535                 
46536                 this.el.dom.selectionStart = curr;
46537                 this.el.dom.selectionEnd = pos;
46538             },
46539
46540             scope : this,
46541
46542             doRelay : function(foo, bar, hname){
46543                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46544             },
46545
46546             forceKeyDown: true
46547         });
46548         
46549 //        if(this.autosave && this.w){
46550 //            this.autoSaveFn = setInterval(this.autosave, 1000);
46551 //        }
46552     },
46553
46554     // private
46555     onResize : function(w, h)
46556     {
46557         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
46558         var ew = false;
46559         var eh = false;
46560         
46561         if(this.el ){
46562             if(typeof w == 'number'){
46563                 var aw = w - this.wrap.getFrameWidth('lr');
46564                 this.el.setWidth(this.adjustWidth('textarea', aw));
46565                 ew = aw;
46566             }
46567             if(typeof h == 'number'){
46568                 var tbh = 0;
46569                 for (var i =0; i < this.toolbars.length;i++) {
46570                     // fixme - ask toolbars for heights?
46571                     tbh += this.toolbars[i].tb.el.getHeight();
46572                     if (this.toolbars[i].footer) {
46573                         tbh += this.toolbars[i].footer.el.getHeight();
46574                     }
46575                 }
46576                 
46577                 
46578                 
46579                 
46580                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
46581                 ah -= 5; // knock a few pixes off for look..
46582 //                Roo.log(ah);
46583                 this.el.setHeight(this.adjustWidth('textarea', ah));
46584                 var eh = ah;
46585             }
46586         }
46587         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
46588         this.editorcore.onResize(ew,eh);
46589         
46590     },
46591
46592     /**
46593      * Toggles the editor between standard and source edit mode.
46594      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46595      */
46596     toggleSourceEdit : function(sourceEditMode)
46597     {
46598         this.editorcore.toggleSourceEdit(sourceEditMode);
46599         
46600         if(this.editorcore.sourceEditMode){
46601             Roo.log('editor - showing textarea');
46602             
46603 //            Roo.log('in');
46604 //            Roo.log(this.syncValue());
46605             this.editorcore.syncValue();
46606             this.el.removeClass('x-hidden');
46607             this.el.dom.removeAttribute('tabIndex');
46608             this.el.focus();
46609             
46610             for (var i = 0; i < this.toolbars.length; i++) {
46611                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46612                     this.toolbars[i].tb.hide();
46613                     this.toolbars[i].footer.hide();
46614                 }
46615             }
46616             
46617         }else{
46618             Roo.log('editor - hiding textarea');
46619 //            Roo.log('out')
46620 //            Roo.log(this.pushValue()); 
46621             this.editorcore.pushValue();
46622             
46623             this.el.addClass('x-hidden');
46624             this.el.dom.setAttribute('tabIndex', -1);
46625             
46626             for (var i = 0; i < this.toolbars.length; i++) {
46627                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46628                     this.toolbars[i].tb.show();
46629                     this.toolbars[i].footer.show();
46630                 }
46631             }
46632             
46633             //this.deferFocus();
46634         }
46635         
46636         this.setSize(this.wrap.getSize());
46637         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
46638         
46639         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
46640     },
46641  
46642     // private (for BoxComponent)
46643     adjustSize : Roo.BoxComponent.prototype.adjustSize,
46644
46645     // private (for BoxComponent)
46646     getResizeEl : function(){
46647         return this.wrap;
46648     },
46649
46650     // private (for BoxComponent)
46651     getPositionEl : function(){
46652         return this.wrap;
46653     },
46654
46655     // private
46656     initEvents : function(){
46657         this.originalValue = this.getValue();
46658     },
46659
46660     /**
46661      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46662      * @method
46663      */
46664     markInvalid : Roo.emptyFn,
46665     /**
46666      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46667      * @method
46668      */
46669     clearInvalid : Roo.emptyFn,
46670
46671     setValue : function(v){
46672         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
46673         this.editorcore.pushValue();
46674     },
46675
46676      
46677     // private
46678     deferFocus : function(){
46679         this.focus.defer(10, this);
46680     },
46681
46682     // doc'ed in Field
46683     focus : function(){
46684         this.editorcore.focus();
46685         
46686     },
46687       
46688
46689     // private
46690     onDestroy : function(){
46691         
46692         
46693         
46694         if(this.rendered){
46695             
46696             for (var i =0; i < this.toolbars.length;i++) {
46697                 // fixme - ask toolbars for heights?
46698                 this.toolbars[i].onDestroy();
46699             }
46700             
46701             this.wrap.dom.innerHTML = '';
46702             this.wrap.remove();
46703         }
46704     },
46705
46706     // private
46707     onFirstFocus : function(){
46708         //Roo.log("onFirstFocus");
46709         this.editorcore.onFirstFocus();
46710          for (var i =0; i < this.toolbars.length;i++) {
46711             this.toolbars[i].onFirstFocus();
46712         }
46713         
46714     },
46715     
46716     // private
46717     syncValue : function()
46718     {
46719         this.editorcore.syncValue();
46720     },
46721     
46722     pushValue : function()
46723     {
46724         this.editorcore.pushValue();
46725     },
46726     
46727     setStylesheets : function(stylesheets)
46728     {
46729         this.editorcore.setStylesheets(stylesheets);
46730     },
46731     
46732     removeStylesheets : function()
46733     {
46734         this.editorcore.removeStylesheets();
46735     }
46736      
46737     
46738     // hide stuff that is not compatible
46739     /**
46740      * @event blur
46741      * @hide
46742      */
46743     /**
46744      * @event change
46745      * @hide
46746      */
46747     /**
46748      * @event focus
46749      * @hide
46750      */
46751     /**
46752      * @event specialkey
46753      * @hide
46754      */
46755     /**
46756      * @cfg {String} fieldClass @hide
46757      */
46758     /**
46759      * @cfg {String} focusClass @hide
46760      */
46761     /**
46762      * @cfg {String} autoCreate @hide
46763      */
46764     /**
46765      * @cfg {String} inputType @hide
46766      */
46767     /**
46768      * @cfg {String} invalidClass @hide
46769      */
46770     /**
46771      * @cfg {String} invalidText @hide
46772      */
46773     /**
46774      * @cfg {String} msgFx @hide
46775      */
46776     /**
46777      * @cfg {String} validateOnBlur @hide
46778      */
46779 });
46780  
46781     // <script type="text/javascript">
46782 /*
46783  * Based on
46784  * Ext JS Library 1.1.1
46785  * Copyright(c) 2006-2007, Ext JS, LLC.
46786  *  
46787  
46788  */
46789
46790 /**
46791  * @class Roo.form.HtmlEditorToolbar1
46792  * Basic Toolbar
46793  * 
46794  * Usage:
46795  *
46796  new Roo.form.HtmlEditor({
46797     ....
46798     toolbars : [
46799         new Roo.form.HtmlEditorToolbar1({
46800             disable : { fonts: 1 , format: 1, ..., ... , ...],
46801             btns : [ .... ]
46802         })
46803     }
46804      
46805  * 
46806  * @cfg {Object} disable List of elements to disable..
46807  * @cfg {Array} btns List of additional buttons.
46808  * 
46809  * 
46810  * NEEDS Extra CSS? 
46811  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46812  */
46813  
46814 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46815 {
46816     
46817     Roo.apply(this, config);
46818     
46819     // default disabled, based on 'good practice'..
46820     this.disable = this.disable || {};
46821     Roo.applyIf(this.disable, {
46822         fontSize : true,
46823         colors : true,
46824         specialElements : true
46825     });
46826     
46827     
46828     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46829     // dont call parent... till later.
46830 }
46831
46832 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46833     
46834     tb: false,
46835     
46836     rendered: false,
46837     
46838     editor : false,
46839     editorcore : false,
46840     /**
46841      * @cfg {Object} disable  List of toolbar elements to disable
46842          
46843      */
46844     disable : false,
46845     
46846     
46847      /**
46848      * @cfg {String} createLinkText The default text for the create link prompt
46849      */
46850     createLinkText : 'Please enter the URL for the link:',
46851     /**
46852      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46853      */
46854     defaultLinkValue : 'http:/'+'/',
46855    
46856     
46857       /**
46858      * @cfg {Array} fontFamilies An array of available font families
46859      */
46860     fontFamilies : [
46861         'Arial',
46862         'Courier New',
46863         'Tahoma',
46864         'Times New Roman',
46865         'Verdana'
46866     ],
46867     
46868     specialChars : [
46869            "&#169;",
46870           "&#174;",     
46871           "&#8482;",    
46872           "&#163;" ,    
46873          // "&#8212;",    
46874           "&#8230;",    
46875           "&#247;" ,    
46876         //  "&#225;" ,     ?? a acute?
46877            "&#8364;"    , //Euro
46878        //   "&#8220;"    ,
46879         //  "&#8221;"    ,
46880         //  "&#8226;"    ,
46881           "&#176;"  //   , // degrees
46882
46883          // "&#233;"     , // e ecute
46884          // "&#250;"     , // u ecute?
46885     ],
46886     
46887     specialElements : [
46888         {
46889             text: "Insert Table",
46890             xtype: 'MenuItem',
46891             xns : Roo.Menu,
46892             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46893                 
46894         },
46895         {    
46896             text: "Insert Image",
46897             xtype: 'MenuItem',
46898             xns : Roo.Menu,
46899             ihtml : '<img src="about:blank"/>'
46900             
46901         }
46902         
46903          
46904     ],
46905     
46906     
46907     inputElements : [ 
46908             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46909             "input:submit", "input:button", "select", "textarea", "label" ],
46910     formats : [
46911         ["p"] ,  
46912         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46913         ["pre"],[ "code"], 
46914         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46915         ['div'],['span'],
46916         ['sup'],['sub']
46917     ],
46918     
46919     cleanStyles : [
46920         "font-size"
46921     ],
46922      /**
46923      * @cfg {String} defaultFont default font to use.
46924      */
46925     defaultFont: 'tahoma',
46926    
46927     fontSelect : false,
46928     
46929     
46930     formatCombo : false,
46931     
46932     init : function(editor)
46933     {
46934         this.editor = editor;
46935         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46936         var editorcore = this.editorcore;
46937         
46938         var _t = this;
46939         
46940         var fid = editorcore.frameId;
46941         var etb = this;
46942         function btn(id, toggle, handler){
46943             var xid = fid + '-'+ id ;
46944             return {
46945                 id : xid,
46946                 cmd : id,
46947                 cls : 'x-btn-icon x-edit-'+id,
46948                 enableToggle:toggle !== false,
46949                 scope: _t, // was editor...
46950                 handler:handler||_t.relayBtnCmd,
46951                 clickEvent:'mousedown',
46952                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46953                 tabIndex:-1
46954             };
46955         }
46956         
46957         
46958         
46959         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46960         this.tb = tb;
46961          // stop form submits
46962         tb.el.on('click', function(e){
46963             e.preventDefault(); // what does this do?
46964         });
46965
46966         if(!this.disable.font) { // && !Roo.isSafari){
46967             /* why no safari for fonts 
46968             editor.fontSelect = tb.el.createChild({
46969                 tag:'select',
46970                 tabIndex: -1,
46971                 cls:'x-font-select',
46972                 html: this.createFontOptions()
46973             });
46974             
46975             editor.fontSelect.on('change', function(){
46976                 var font = editor.fontSelect.dom.value;
46977                 editor.relayCmd('fontname', font);
46978                 editor.deferFocus();
46979             }, editor);
46980             
46981             tb.add(
46982                 editor.fontSelect.dom,
46983                 '-'
46984             );
46985             */
46986             
46987         };
46988         if(!this.disable.formats){
46989             this.formatCombo = new Roo.form.ComboBox({
46990                 store: new Roo.data.SimpleStore({
46991                     id : 'tag',
46992                     fields: ['tag'],
46993                     data : this.formats // from states.js
46994                 }),
46995                 blockFocus : true,
46996                 name : '',
46997                 //autoCreate : {tag: "div",  size: "20"},
46998                 displayField:'tag',
46999                 typeAhead: false,
47000                 mode: 'local',
47001                 editable : false,
47002                 triggerAction: 'all',
47003                 emptyText:'Add tag',
47004                 selectOnFocus:true,
47005                 width:135,
47006                 listeners : {
47007                     'select': function(c, r, i) {
47008                         editorcore.insertTag(r.get('tag'));
47009                         editor.focus();
47010                     }
47011                 }
47012
47013             });
47014             tb.addField(this.formatCombo);
47015             
47016         }
47017         
47018         if(!this.disable.format){
47019             tb.add(
47020                 btn('bold'),
47021                 btn('italic'),
47022                 btn('underline'),
47023                 btn('strikethrough')
47024             );
47025         };
47026         if(!this.disable.fontSize){
47027             tb.add(
47028                 '-',
47029                 
47030                 
47031                 btn('increasefontsize', false, editorcore.adjustFont),
47032                 btn('decreasefontsize', false, editorcore.adjustFont)
47033             );
47034         };
47035         
47036         
47037         if(!this.disable.colors){
47038             tb.add(
47039                 '-', {
47040                     id:editorcore.frameId +'-forecolor',
47041                     cls:'x-btn-icon x-edit-forecolor',
47042                     clickEvent:'mousedown',
47043                     tooltip: this.buttonTips['forecolor'] || undefined,
47044                     tabIndex:-1,
47045                     menu : new Roo.menu.ColorMenu({
47046                         allowReselect: true,
47047                         focus: Roo.emptyFn,
47048                         value:'000000',
47049                         plain:true,
47050                         selectHandler: function(cp, color){
47051                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47052                             editor.deferFocus();
47053                         },
47054                         scope: editorcore,
47055                         clickEvent:'mousedown'
47056                     })
47057                 }, {
47058                     id:editorcore.frameId +'backcolor',
47059                     cls:'x-btn-icon x-edit-backcolor',
47060                     clickEvent:'mousedown',
47061                     tooltip: this.buttonTips['backcolor'] || undefined,
47062                     tabIndex:-1,
47063                     menu : new Roo.menu.ColorMenu({
47064                         focus: Roo.emptyFn,
47065                         value:'FFFFFF',
47066                         plain:true,
47067                         allowReselect: true,
47068                         selectHandler: function(cp, color){
47069                             if(Roo.isGecko){
47070                                 editorcore.execCmd('useCSS', false);
47071                                 editorcore.execCmd('hilitecolor', color);
47072                                 editorcore.execCmd('useCSS', true);
47073                                 editor.deferFocus();
47074                             }else{
47075                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47076                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47077                                 editor.deferFocus();
47078                             }
47079                         },
47080                         scope:editorcore,
47081                         clickEvent:'mousedown'
47082                     })
47083                 }
47084             );
47085         };
47086         // now add all the items...
47087         
47088
47089         if(!this.disable.alignments){
47090             tb.add(
47091                 '-',
47092                 btn('justifyleft'),
47093                 btn('justifycenter'),
47094                 btn('justifyright')
47095             );
47096         };
47097
47098         //if(!Roo.isSafari){
47099             if(!this.disable.links){
47100                 tb.add(
47101                     '-',
47102                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47103                 );
47104             };
47105
47106             if(!this.disable.lists){
47107                 tb.add(
47108                     '-',
47109                     btn('insertorderedlist'),
47110                     btn('insertunorderedlist')
47111                 );
47112             }
47113             if(!this.disable.sourceEdit){
47114                 tb.add(
47115                     '-',
47116                     btn('sourceedit', true, function(btn){
47117                         this.toggleSourceEdit(btn.pressed);
47118                     })
47119                 );
47120             }
47121         //}
47122         
47123         var smenu = { };
47124         // special menu.. - needs to be tidied up..
47125         if (!this.disable.special) {
47126             smenu = {
47127                 text: "&#169;",
47128                 cls: 'x-edit-none',
47129                 
47130                 menu : {
47131                     items : []
47132                 }
47133             };
47134             for (var i =0; i < this.specialChars.length; i++) {
47135                 smenu.menu.items.push({
47136                     
47137                     html: this.specialChars[i],
47138                     handler: function(a,b) {
47139                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47140                         //editor.insertAtCursor(a.html);
47141                         
47142                     },
47143                     tabIndex:-1
47144                 });
47145             }
47146             
47147             
47148             tb.add(smenu);
47149             
47150             
47151         }
47152         
47153         var cmenu = { };
47154         if (!this.disable.cleanStyles) {
47155             cmenu = {
47156                 cls: 'x-btn-icon x-btn-clear',
47157                 
47158                 menu : {
47159                     items : []
47160                 }
47161             };
47162             for (var i =0; i < this.cleanStyles.length; i++) {
47163                 cmenu.menu.items.push({
47164                     actiontype : this.cleanStyles[i],
47165                     html: 'Remove ' + this.cleanStyles[i],
47166                     handler: function(a,b) {
47167 //                        Roo.log(a);
47168 //                        Roo.log(b);
47169                         var c = Roo.get(editorcore.doc.body);
47170                         c.select('[style]').each(function(s) {
47171                             s.dom.style.removeProperty(a.actiontype);
47172                         });
47173                         editorcore.syncValue();
47174                     },
47175                     tabIndex:-1
47176                 });
47177             }
47178              cmenu.menu.items.push({
47179                 actiontype : 'tablewidths',
47180                 html: 'Remove Table Widths',
47181                 handler: function(a,b) {
47182                     editorcore.cleanTableWidths();
47183                     editorcore.syncValue();
47184                 },
47185                 tabIndex:-1
47186             });
47187             cmenu.menu.items.push({
47188                 actiontype : 'word',
47189                 html: 'Remove MS Word Formating',
47190                 handler: function(a,b) {
47191                     editorcore.cleanWord();
47192                     editorcore.syncValue();
47193                 },
47194                 tabIndex:-1
47195             });
47196             
47197             cmenu.menu.items.push({
47198                 actiontype : 'all',
47199                 html: 'Remove All Styles',
47200                 handler: function(a,b) {
47201                     
47202                     var c = Roo.get(editorcore.doc.body);
47203                     c.select('[style]').each(function(s) {
47204                         s.dom.removeAttribute('style');
47205                     });
47206                     editorcore.syncValue();
47207                 },
47208                 tabIndex:-1
47209             });
47210             
47211             cmenu.menu.items.push({
47212                 actiontype : 'all',
47213                 html: 'Remove All CSS Classes',
47214                 handler: function(a,b) {
47215                     
47216                     var c = Roo.get(editorcore.doc.body);
47217                     c.select('[class]').each(function(s) {
47218                         s.dom.removeAttribute('class');
47219                     });
47220                     editorcore.cleanWord();
47221                     editorcore.syncValue();
47222                 },
47223                 tabIndex:-1
47224             });
47225             
47226              cmenu.menu.items.push({
47227                 actiontype : 'tidy',
47228                 html: 'Tidy HTML Source',
47229                 handler: function(a,b) {
47230                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
47231                     editorcore.syncValue();
47232                 },
47233                 tabIndex:-1
47234             });
47235             
47236             
47237             tb.add(cmenu);
47238         }
47239          
47240         if (!this.disable.specialElements) {
47241             var semenu = {
47242                 text: "Other;",
47243                 cls: 'x-edit-none',
47244                 menu : {
47245                     items : []
47246                 }
47247             };
47248             for (var i =0; i < this.specialElements.length; i++) {
47249                 semenu.menu.items.push(
47250                     Roo.apply({ 
47251                         handler: function(a,b) {
47252                             editor.insertAtCursor(this.ihtml);
47253                         }
47254                     }, this.specialElements[i])
47255                 );
47256                     
47257             }
47258             
47259             tb.add(semenu);
47260             
47261             
47262         }
47263          
47264         
47265         if (this.btns) {
47266             for(var i =0; i< this.btns.length;i++) {
47267                 var b = Roo.factory(this.btns[i],Roo.form);
47268                 b.cls =  'x-edit-none';
47269                 
47270                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47271                     b.cls += ' x-init-enable';
47272                 }
47273                 
47274                 b.scope = editorcore;
47275                 tb.add(b);
47276             }
47277         
47278         }
47279         
47280         
47281         
47282         // disable everything...
47283         
47284         this.tb.items.each(function(item){
47285             
47286            if(
47287                 item.id != editorcore.frameId+ '-sourceedit' && 
47288                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47289             ){
47290                 
47291                 item.disable();
47292             }
47293         });
47294         this.rendered = true;
47295         
47296         // the all the btns;
47297         editor.on('editorevent', this.updateToolbar, this);
47298         // other toolbars need to implement this..
47299         //editor.on('editmodechange', this.updateToolbar, this);
47300     },
47301     
47302     
47303     relayBtnCmd : function(btn) {
47304         this.editorcore.relayCmd(btn.cmd);
47305     },
47306     // private used internally
47307     createLink : function(){
47308         Roo.log("create link?");
47309         var url = prompt(this.createLinkText, this.defaultLinkValue);
47310         if(url && url != 'http:/'+'/'){
47311             this.editorcore.relayCmd('createlink', url);
47312         }
47313     },
47314
47315     
47316     /**
47317      * Protected method that will not generally be called directly. It triggers
47318      * a toolbar update by reading the markup state of the current selection in the editor.
47319      */
47320     updateToolbar: function(){
47321
47322         if(!this.editorcore.activated){
47323             this.editor.onFirstFocus();
47324             return;
47325         }
47326
47327         var btns = this.tb.items.map, 
47328             doc = this.editorcore.doc,
47329             frameId = this.editorcore.frameId;
47330
47331         if(!this.disable.font && !Roo.isSafari){
47332             /*
47333             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47334             if(name != this.fontSelect.dom.value){
47335                 this.fontSelect.dom.value = name;
47336             }
47337             */
47338         }
47339         if(!this.disable.format){
47340             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47341             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47342             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47343             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47344         }
47345         if(!this.disable.alignments){
47346             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47347             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47348             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47349         }
47350         if(!Roo.isSafari && !this.disable.lists){
47351             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47352             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47353         }
47354         
47355         var ans = this.editorcore.getAllAncestors();
47356         if (this.formatCombo) {
47357             
47358             
47359             var store = this.formatCombo.store;
47360             this.formatCombo.setValue("");
47361             for (var i =0; i < ans.length;i++) {
47362                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47363                     // select it..
47364                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47365                     break;
47366                 }
47367             }
47368         }
47369         
47370         
47371         
47372         // hides menus... - so this cant be on a menu...
47373         Roo.menu.MenuMgr.hideAll();
47374
47375         //this.editorsyncValue();
47376     },
47377    
47378     
47379     createFontOptions : function(){
47380         var buf = [], fs = this.fontFamilies, ff, lc;
47381         
47382         
47383         
47384         for(var i = 0, len = fs.length; i< len; i++){
47385             ff = fs[i];
47386             lc = ff.toLowerCase();
47387             buf.push(
47388                 '<option value="',lc,'" style="font-family:',ff,';"',
47389                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47390                     ff,
47391                 '</option>'
47392             );
47393         }
47394         return buf.join('');
47395     },
47396     
47397     toggleSourceEdit : function(sourceEditMode){
47398         
47399         Roo.log("toolbar toogle");
47400         if(sourceEditMode === undefined){
47401             sourceEditMode = !this.sourceEditMode;
47402         }
47403         this.sourceEditMode = sourceEditMode === true;
47404         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47405         // just toggle the button?
47406         if(btn.pressed !== this.sourceEditMode){
47407             btn.toggle(this.sourceEditMode);
47408             return;
47409         }
47410         
47411         if(sourceEditMode){
47412             Roo.log("disabling buttons");
47413             this.tb.items.each(function(item){
47414                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47415                     item.disable();
47416                 }
47417             });
47418           
47419         }else{
47420             Roo.log("enabling buttons");
47421             if(this.editorcore.initialized){
47422                 this.tb.items.each(function(item){
47423                     item.enable();
47424                 });
47425             }
47426             
47427         }
47428         Roo.log("calling toggole on editor");
47429         // tell the editor that it's been pressed..
47430         this.editor.toggleSourceEdit(sourceEditMode);
47431        
47432     },
47433      /**
47434      * Object collection of toolbar tooltips for the buttons in the editor. The key
47435      * is the command id associated with that button and the value is a valid QuickTips object.
47436      * For example:
47437 <pre><code>
47438 {
47439     bold : {
47440         title: 'Bold (Ctrl+B)',
47441         text: 'Make the selected text bold.',
47442         cls: 'x-html-editor-tip'
47443     },
47444     italic : {
47445         title: 'Italic (Ctrl+I)',
47446         text: 'Make the selected text italic.',
47447         cls: 'x-html-editor-tip'
47448     },
47449     ...
47450 </code></pre>
47451     * @type Object
47452      */
47453     buttonTips : {
47454         bold : {
47455             title: 'Bold (Ctrl+B)',
47456             text: 'Make the selected text bold.',
47457             cls: 'x-html-editor-tip'
47458         },
47459         italic : {
47460             title: 'Italic (Ctrl+I)',
47461             text: 'Make the selected text italic.',
47462             cls: 'x-html-editor-tip'
47463         },
47464         underline : {
47465             title: 'Underline (Ctrl+U)',
47466             text: 'Underline the selected text.',
47467             cls: 'x-html-editor-tip'
47468         },
47469         strikethrough : {
47470             title: 'Strikethrough',
47471             text: 'Strikethrough the selected text.',
47472             cls: 'x-html-editor-tip'
47473         },
47474         increasefontsize : {
47475             title: 'Grow Text',
47476             text: 'Increase the font size.',
47477             cls: 'x-html-editor-tip'
47478         },
47479         decreasefontsize : {
47480             title: 'Shrink Text',
47481             text: 'Decrease the font size.',
47482             cls: 'x-html-editor-tip'
47483         },
47484         backcolor : {
47485             title: 'Text Highlight Color',
47486             text: 'Change the background color of the selected text.',
47487             cls: 'x-html-editor-tip'
47488         },
47489         forecolor : {
47490             title: 'Font Color',
47491             text: 'Change the color of the selected text.',
47492             cls: 'x-html-editor-tip'
47493         },
47494         justifyleft : {
47495             title: 'Align Text Left',
47496             text: 'Align text to the left.',
47497             cls: 'x-html-editor-tip'
47498         },
47499         justifycenter : {
47500             title: 'Center Text',
47501             text: 'Center text in the editor.',
47502             cls: 'x-html-editor-tip'
47503         },
47504         justifyright : {
47505             title: 'Align Text Right',
47506             text: 'Align text to the right.',
47507             cls: 'x-html-editor-tip'
47508         },
47509         insertunorderedlist : {
47510             title: 'Bullet List',
47511             text: 'Start a bulleted list.',
47512             cls: 'x-html-editor-tip'
47513         },
47514         insertorderedlist : {
47515             title: 'Numbered List',
47516             text: 'Start a numbered list.',
47517             cls: 'x-html-editor-tip'
47518         },
47519         createlink : {
47520             title: 'Hyperlink',
47521             text: 'Make the selected text a hyperlink.',
47522             cls: 'x-html-editor-tip'
47523         },
47524         sourceedit : {
47525             title: 'Source Edit',
47526             text: 'Switch to source editing mode.',
47527             cls: 'x-html-editor-tip'
47528         }
47529     },
47530     // private
47531     onDestroy : function(){
47532         if(this.rendered){
47533             
47534             this.tb.items.each(function(item){
47535                 if(item.menu){
47536                     item.menu.removeAll();
47537                     if(item.menu.el){
47538                         item.menu.el.destroy();
47539                     }
47540                 }
47541                 item.destroy();
47542             });
47543              
47544         }
47545     },
47546     onFirstFocus: function() {
47547         this.tb.items.each(function(item){
47548            item.enable();
47549         });
47550     }
47551 });
47552
47553
47554
47555
47556 // <script type="text/javascript">
47557 /*
47558  * Based on
47559  * Ext JS Library 1.1.1
47560  * Copyright(c) 2006-2007, Ext JS, LLC.
47561  *  
47562  
47563  */
47564
47565  
47566 /**
47567  * @class Roo.form.HtmlEditor.ToolbarContext
47568  * Context Toolbar
47569  * 
47570  * Usage:
47571  *
47572  new Roo.form.HtmlEditor({
47573     ....
47574     toolbars : [
47575         { xtype: 'ToolbarStandard', styles : {} }
47576         { xtype: 'ToolbarContext', disable : {} }
47577     ]
47578 })
47579
47580      
47581  * 
47582  * @config : {Object} disable List of elements to disable.. (not done yet.)
47583  * @config : {Object} styles  Map of styles available.
47584  * 
47585  */
47586
47587 Roo.form.HtmlEditor.ToolbarContext = function(config)
47588 {
47589     
47590     Roo.apply(this, config);
47591     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47592     // dont call parent... till later.
47593     this.styles = this.styles || {};
47594 }
47595
47596  
47597
47598 Roo.form.HtmlEditor.ToolbarContext.types = {
47599     'IMG' : {
47600         width : {
47601             title: "Width",
47602             width: 40
47603         },
47604         height:  {
47605             title: "Height",
47606             width: 40
47607         },
47608         align: {
47609             title: "Align",
47610             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47611             width : 80
47612             
47613         },
47614         border: {
47615             title: "Border",
47616             width: 40
47617         },
47618         alt: {
47619             title: "Alt",
47620             width: 120
47621         },
47622         src : {
47623             title: "Src",
47624             width: 220
47625         }
47626         
47627     },
47628     'A' : {
47629         name : {
47630             title: "Name",
47631             width: 50
47632         },
47633         target:  {
47634             title: "Target",
47635             width: 120
47636         },
47637         href:  {
47638             title: "Href",
47639             width: 220
47640         } // border?
47641         
47642     },
47643     'TABLE' : {
47644         rows : {
47645             title: "Rows",
47646             width: 20
47647         },
47648         cols : {
47649             title: "Cols",
47650             width: 20
47651         },
47652         width : {
47653             title: "Width",
47654             width: 40
47655         },
47656         height : {
47657             title: "Height",
47658             width: 40
47659         },
47660         border : {
47661             title: "Border",
47662             width: 20
47663         }
47664     },
47665     'TD' : {
47666         width : {
47667             title: "Width",
47668             width: 40
47669         },
47670         height : {
47671             title: "Height",
47672             width: 40
47673         },   
47674         align: {
47675             title: "Align",
47676             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
47677             width: 80
47678         },
47679         valign: {
47680             title: "Valign",
47681             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
47682             width: 80
47683         },
47684         colspan: {
47685             title: "Colspan",
47686             width: 20
47687             
47688         },
47689          'font-family'  : {
47690             title : "Font",
47691             style : 'fontFamily',
47692             displayField: 'display',
47693             optname : 'font-family',
47694             width: 140
47695         }
47696     },
47697     'INPUT' : {
47698         name : {
47699             title: "name",
47700             width: 120
47701         },
47702         value : {
47703             title: "Value",
47704             width: 120
47705         },
47706         width : {
47707             title: "Width",
47708             width: 40
47709         }
47710     },
47711     'LABEL' : {
47712         'for' : {
47713             title: "For",
47714             width: 120
47715         }
47716     },
47717     'TEXTAREA' : {
47718           name : {
47719             title: "name",
47720             width: 120
47721         },
47722         rows : {
47723             title: "Rows",
47724             width: 20
47725         },
47726         cols : {
47727             title: "Cols",
47728             width: 20
47729         }
47730     },
47731     'SELECT' : {
47732         name : {
47733             title: "name",
47734             width: 120
47735         },
47736         selectoptions : {
47737             title: "Options",
47738             width: 200
47739         }
47740     },
47741     
47742     // should we really allow this??
47743     // should this just be 
47744     'BODY' : {
47745         title : {
47746             title: "Title",
47747             width: 200,
47748             disabled : true
47749         }
47750     },
47751     'SPAN' : {
47752         'font-family'  : {
47753             title : "Font",
47754             style : 'fontFamily',
47755             displayField: 'display',
47756             optname : 'font-family',
47757             width: 140
47758         }
47759     },
47760     'DIV' : {
47761         'font-family'  : {
47762             title : "Font",
47763             style : 'fontFamily',
47764             displayField: 'display',
47765             optname : 'font-family',
47766             width: 140
47767         }
47768     },
47769      'P' : {
47770         'font-family'  : {
47771             title : "Font",
47772             style : 'fontFamily',
47773             displayField: 'display',
47774             optname : 'font-family',
47775             width: 140
47776         }
47777     },
47778     
47779     '*' : {
47780         // empty..
47781     }
47782
47783 };
47784
47785 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47786 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47787
47788 Roo.form.HtmlEditor.ToolbarContext.options = {
47789         'font-family'  : [ 
47790                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47791                 [ 'Courier New', 'Courier New'],
47792                 [ 'Tahoma', 'Tahoma'],
47793                 [ 'Times New Roman,serif', 'Times'],
47794                 [ 'Verdana','Verdana' ]
47795         ]
47796 };
47797
47798 // fixme - these need to be configurable..
47799  
47800
47801 //Roo.form.HtmlEditor.ToolbarContext.types
47802
47803
47804 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47805     
47806     tb: false,
47807     
47808     rendered: false,
47809     
47810     editor : false,
47811     editorcore : false,
47812     /**
47813      * @cfg {Object} disable  List of toolbar elements to disable
47814          
47815      */
47816     disable : false,
47817     /**
47818      * @cfg {Object} styles List of styles 
47819      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47820      *
47821      * These must be defined in the page, so they get rendered correctly..
47822      * .headline { }
47823      * TD.underline { }
47824      * 
47825      */
47826     styles : false,
47827     
47828     options: false,
47829     
47830     toolbars : false,
47831     
47832     init : function(editor)
47833     {
47834         this.editor = editor;
47835         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47836         var editorcore = this.editorcore;
47837         
47838         var fid = editorcore.frameId;
47839         var etb = this;
47840         function btn(id, toggle, handler){
47841             var xid = fid + '-'+ id ;
47842             return {
47843                 id : xid,
47844                 cmd : id,
47845                 cls : 'x-btn-icon x-edit-'+id,
47846                 enableToggle:toggle !== false,
47847                 scope: editorcore, // was editor...
47848                 handler:handler||editorcore.relayBtnCmd,
47849                 clickEvent:'mousedown',
47850                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47851                 tabIndex:-1
47852             };
47853         }
47854         // create a new element.
47855         var wdiv = editor.wrap.createChild({
47856                 tag: 'div'
47857             }, editor.wrap.dom.firstChild.nextSibling, true);
47858         
47859         // can we do this more than once??
47860         
47861          // stop form submits
47862       
47863  
47864         // disable everything...
47865         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47866         this.toolbars = {};
47867            
47868         for (var i in  ty) {
47869           
47870             this.toolbars[i] = this.buildToolbar(ty[i],i);
47871         }
47872         this.tb = this.toolbars.BODY;
47873         this.tb.el.show();
47874         this.buildFooter();
47875         this.footer.show();
47876         editor.on('hide', function( ) { this.footer.hide() }, this);
47877         editor.on('show', function( ) { this.footer.show() }, this);
47878         
47879          
47880         this.rendered = true;
47881         
47882         // the all the btns;
47883         editor.on('editorevent', this.updateToolbar, this);
47884         // other toolbars need to implement this..
47885         //editor.on('editmodechange', this.updateToolbar, this);
47886     },
47887     
47888     
47889     
47890     /**
47891      * Protected method that will not generally be called directly. It triggers
47892      * a toolbar update by reading the markup state of the current selection in the editor.
47893      *
47894      * Note you can force an update by calling on('editorevent', scope, false)
47895      */
47896     updateToolbar: function(editor,ev,sel){
47897
47898         //Roo.log(ev);
47899         // capture mouse up - this is handy for selecting images..
47900         // perhaps should go somewhere else...
47901         if(!this.editorcore.activated){
47902              this.editor.onFirstFocus();
47903             return;
47904         }
47905         
47906         
47907         
47908         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47909         // selectNode - might want to handle IE?
47910         if (ev &&
47911             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47912             ev.target && ev.target.tagName == 'IMG') {
47913             // they have click on an image...
47914             // let's see if we can change the selection...
47915             sel = ev.target;
47916          
47917               var nodeRange = sel.ownerDocument.createRange();
47918             try {
47919                 nodeRange.selectNode(sel);
47920             } catch (e) {
47921                 nodeRange.selectNodeContents(sel);
47922             }
47923             //nodeRange.collapse(true);
47924             var s = this.editorcore.win.getSelection();
47925             s.removeAllRanges();
47926             s.addRange(nodeRange);
47927         }  
47928         
47929       
47930         var updateFooter = sel ? false : true;
47931         
47932         
47933         var ans = this.editorcore.getAllAncestors();
47934         
47935         // pick
47936         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47937         
47938         if (!sel) { 
47939             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47940             sel = sel ? sel : this.editorcore.doc.body;
47941             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47942             
47943         }
47944         // pick a menu that exists..
47945         var tn = sel.tagName.toUpperCase();
47946         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47947         
47948         tn = sel.tagName.toUpperCase();
47949         
47950         var lastSel = this.tb.selectedNode;
47951         
47952         this.tb.selectedNode = sel;
47953         
47954         // if current menu does not match..
47955         
47956         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47957                 
47958             this.tb.el.hide();
47959             ///console.log("show: " + tn);
47960             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47961             this.tb.el.show();
47962             // update name
47963             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47964             
47965             
47966             // update attributes
47967             if (this.tb.fields) {
47968                 this.tb.fields.each(function(e) {
47969                     if (e.stylename) {
47970                         e.setValue(sel.style[e.stylename]);
47971                         return;
47972                     } 
47973                    e.setValue(sel.getAttribute(e.attrname));
47974                 });
47975             }
47976             
47977             var hasStyles = false;
47978             for(var i in this.styles) {
47979                 hasStyles = true;
47980                 break;
47981             }
47982             
47983             // update styles
47984             if (hasStyles) { 
47985                 var st = this.tb.fields.item(0);
47986                 
47987                 st.store.removeAll();
47988                
47989                 
47990                 var cn = sel.className.split(/\s+/);
47991                 
47992                 var avs = [];
47993                 if (this.styles['*']) {
47994                     
47995                     Roo.each(this.styles['*'], function(v) {
47996                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47997                     });
47998                 }
47999                 if (this.styles[tn]) { 
48000                     Roo.each(this.styles[tn], function(v) {
48001                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48002                     });
48003                 }
48004                 
48005                 st.store.loadData(avs);
48006                 st.collapse();
48007                 st.setValue(cn);
48008             }
48009             // flag our selected Node.
48010             this.tb.selectedNode = sel;
48011            
48012            
48013             Roo.menu.MenuMgr.hideAll();
48014
48015         }
48016         
48017         if (!updateFooter) {
48018             //this.footDisp.dom.innerHTML = ''; 
48019             return;
48020         }
48021         // update the footer
48022         //
48023         var html = '';
48024         
48025         this.footerEls = ans.reverse();
48026         Roo.each(this.footerEls, function(a,i) {
48027             if (!a) { return; }
48028             html += html.length ? ' &gt; '  :  '';
48029             
48030             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48031             
48032         });
48033        
48034         // 
48035         var sz = this.footDisp.up('td').getSize();
48036         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48037         this.footDisp.dom.style.marginLeft = '5px';
48038         
48039         this.footDisp.dom.style.overflow = 'hidden';
48040         
48041         this.footDisp.dom.innerHTML = html;
48042             
48043         //this.editorsyncValue();
48044     },
48045      
48046     
48047    
48048        
48049     // private
48050     onDestroy : function(){
48051         if(this.rendered){
48052             
48053             this.tb.items.each(function(item){
48054                 if(item.menu){
48055                     item.menu.removeAll();
48056                     if(item.menu.el){
48057                         item.menu.el.destroy();
48058                     }
48059                 }
48060                 item.destroy();
48061             });
48062              
48063         }
48064     },
48065     onFirstFocus: function() {
48066         // need to do this for all the toolbars..
48067         this.tb.items.each(function(item){
48068            item.enable();
48069         });
48070     },
48071     buildToolbar: function(tlist, nm)
48072     {
48073         var editor = this.editor;
48074         var editorcore = this.editorcore;
48075          // create a new element.
48076         var wdiv = editor.wrap.createChild({
48077                 tag: 'div'
48078             }, editor.wrap.dom.firstChild.nextSibling, true);
48079         
48080        
48081         var tb = new Roo.Toolbar(wdiv);
48082         // add the name..
48083         
48084         tb.add(nm+ ":&nbsp;");
48085         
48086         var styles = [];
48087         for(var i in this.styles) {
48088             styles.push(i);
48089         }
48090         
48091         // styles...
48092         if (styles && styles.length) {
48093             
48094             // this needs a multi-select checkbox...
48095             tb.addField( new Roo.form.ComboBox({
48096                 store: new Roo.data.SimpleStore({
48097                     id : 'val',
48098                     fields: ['val', 'selected'],
48099                     data : [] 
48100                 }),
48101                 name : '-roo-edit-className',
48102                 attrname : 'className',
48103                 displayField: 'val',
48104                 typeAhead: false,
48105                 mode: 'local',
48106                 editable : false,
48107                 triggerAction: 'all',
48108                 emptyText:'Select Style',
48109                 selectOnFocus:true,
48110                 width: 130,
48111                 listeners : {
48112                     'select': function(c, r, i) {
48113                         // initial support only for on class per el..
48114                         tb.selectedNode.className =  r ? r.get('val') : '';
48115                         editorcore.syncValue();
48116                     }
48117                 }
48118     
48119             }));
48120         }
48121         
48122         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48123         var tbops = tbc.options;
48124         
48125         for (var i in tlist) {
48126             
48127             var item = tlist[i];
48128             tb.add(item.title + ":&nbsp;");
48129             
48130             
48131             //optname == used so you can configure the options available..
48132             var opts = item.opts ? item.opts : false;
48133             if (item.optname) {
48134                 opts = tbops[item.optname];
48135            
48136             }
48137             
48138             if (opts) {
48139                 // opts == pulldown..
48140                 tb.addField( new Roo.form.ComboBox({
48141                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48142                         id : 'val',
48143                         fields: ['val', 'display'],
48144                         data : opts  
48145                     }),
48146                     name : '-roo-edit-' + i,
48147                     attrname : i,
48148                     stylename : item.style ? item.style : false,
48149                     displayField: item.displayField ? item.displayField : 'val',
48150                     valueField :  'val',
48151                     typeAhead: false,
48152                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48153                     editable : false,
48154                     triggerAction: 'all',
48155                     emptyText:'Select',
48156                     selectOnFocus:true,
48157                     width: item.width ? item.width  : 130,
48158                     listeners : {
48159                         'select': function(c, r, i) {
48160                             if (c.stylename) {
48161                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48162                                 return;
48163                             }
48164                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48165                         }
48166                     }
48167
48168                 }));
48169                 continue;
48170                     
48171                  
48172                 
48173                 tb.addField( new Roo.form.TextField({
48174                     name: i,
48175                     width: 100,
48176                     //allowBlank:false,
48177                     value: ''
48178                 }));
48179                 continue;
48180             }
48181             tb.addField( new Roo.form.TextField({
48182                 name: '-roo-edit-' + i,
48183                 attrname : i,
48184                 
48185                 width: item.width,
48186                 //allowBlank:true,
48187                 value: '',
48188                 listeners: {
48189                     'change' : function(f, nv, ov) {
48190                         tb.selectedNode.setAttribute(f.attrname, nv);
48191                         editorcore.syncValue();
48192                     }
48193                 }
48194             }));
48195              
48196         }
48197         
48198         var _this = this;
48199         
48200         if(nm == 'BODY'){
48201             tb.addSeparator();
48202         
48203             tb.addButton( {
48204                 text: 'Stylesheets',
48205
48206                 listeners : {
48207                     click : function ()
48208                     {
48209                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48210                     }
48211                 }
48212             });
48213         }
48214         
48215         tb.addFill();
48216         tb.addButton( {
48217             text: 'Remove Tag',
48218     
48219             listeners : {
48220                 click : function ()
48221                 {
48222                     // remove
48223                     // undo does not work.
48224                      
48225                     var sn = tb.selectedNode;
48226                     
48227                     var pn = sn.parentNode;
48228                     
48229                     var stn =  sn.childNodes[0];
48230                     var en = sn.childNodes[sn.childNodes.length - 1 ];
48231                     while (sn.childNodes.length) {
48232                         var node = sn.childNodes[0];
48233                         sn.removeChild(node);
48234                         //Roo.log(node);
48235                         pn.insertBefore(node, sn);
48236                         
48237                     }
48238                     pn.removeChild(sn);
48239                     var range = editorcore.createRange();
48240         
48241                     range.setStart(stn,0);
48242                     range.setEnd(en,0); //????
48243                     //range.selectNode(sel);
48244                     
48245                     
48246                     var selection = editorcore.getSelection();
48247                     selection.removeAllRanges();
48248                     selection.addRange(range);
48249                     
48250                     
48251                     
48252                     //_this.updateToolbar(null, null, pn);
48253                     _this.updateToolbar(null, null, null);
48254                     _this.footDisp.dom.innerHTML = ''; 
48255                 }
48256             }
48257             
48258                     
48259                 
48260             
48261         });
48262         
48263         
48264         tb.el.on('click', function(e){
48265             e.preventDefault(); // what does this do?
48266         });
48267         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48268         tb.el.hide();
48269         tb.name = nm;
48270         // dont need to disable them... as they will get hidden
48271         return tb;
48272          
48273         
48274     },
48275     buildFooter : function()
48276     {
48277         
48278         var fel = this.editor.wrap.createChild();
48279         this.footer = new Roo.Toolbar(fel);
48280         // toolbar has scrolly on left / right?
48281         var footDisp= new Roo.Toolbar.Fill();
48282         var _t = this;
48283         this.footer.add(
48284             {
48285                 text : '&lt;',
48286                 xtype: 'Button',
48287                 handler : function() {
48288                     _t.footDisp.scrollTo('left',0,true)
48289                 }
48290             }
48291         );
48292         this.footer.add( footDisp );
48293         this.footer.add( 
48294             {
48295                 text : '&gt;',
48296                 xtype: 'Button',
48297                 handler : function() {
48298                     // no animation..
48299                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48300                 }
48301             }
48302         );
48303         var fel = Roo.get(footDisp.el);
48304         fel.addClass('x-editor-context');
48305         this.footDispWrap = fel; 
48306         this.footDispWrap.overflow  = 'hidden';
48307         
48308         this.footDisp = fel.createChild();
48309         this.footDispWrap.on('click', this.onContextClick, this)
48310         
48311         
48312     },
48313     onContextClick : function (ev,dom)
48314     {
48315         ev.preventDefault();
48316         var  cn = dom.className;
48317         //Roo.log(cn);
48318         if (!cn.match(/x-ed-loc-/)) {
48319             return;
48320         }
48321         var n = cn.split('-').pop();
48322         var ans = this.footerEls;
48323         var sel = ans[n];
48324         
48325          // pick
48326         var range = this.editorcore.createRange();
48327         
48328         range.selectNodeContents(sel);
48329         //range.selectNode(sel);
48330         
48331         
48332         var selection = this.editorcore.getSelection();
48333         selection.removeAllRanges();
48334         selection.addRange(range);
48335         
48336         
48337         
48338         this.updateToolbar(null, null, sel);
48339         
48340         
48341     }
48342     
48343     
48344     
48345     
48346     
48347 });
48348
48349
48350
48351
48352
48353 /*
48354  * Based on:
48355  * Ext JS Library 1.1.1
48356  * Copyright(c) 2006-2007, Ext JS, LLC.
48357  *
48358  * Originally Released Under LGPL - original licence link has changed is not relivant.
48359  *
48360  * Fork - LGPL
48361  * <script type="text/javascript">
48362  */
48363  
48364 /**
48365  * @class Roo.form.BasicForm
48366  * @extends Roo.util.Observable
48367  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48368  * @constructor
48369  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48370  * @param {Object} config Configuration options
48371  */
48372 Roo.form.BasicForm = function(el, config){
48373     this.allItems = [];
48374     this.childForms = [];
48375     Roo.apply(this, config);
48376     /*
48377      * The Roo.form.Field items in this form.
48378      * @type MixedCollection
48379      */
48380      
48381      
48382     this.items = new Roo.util.MixedCollection(false, function(o){
48383         return o.id || (o.id = Roo.id());
48384     });
48385     this.addEvents({
48386         /**
48387          * @event beforeaction
48388          * Fires before any action is performed. Return false to cancel the action.
48389          * @param {Form} this
48390          * @param {Action} action The action to be performed
48391          */
48392         beforeaction: true,
48393         /**
48394          * @event actionfailed
48395          * Fires when an action fails.
48396          * @param {Form} this
48397          * @param {Action} action The action that failed
48398          */
48399         actionfailed : true,
48400         /**
48401          * @event actioncomplete
48402          * Fires when an action is completed.
48403          * @param {Form} this
48404          * @param {Action} action The action that completed
48405          */
48406         actioncomplete : true
48407     });
48408     if(el){
48409         this.initEl(el);
48410     }
48411     Roo.form.BasicForm.superclass.constructor.call(this);
48412     
48413     Roo.form.BasicForm.popover.apply();
48414 };
48415
48416 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48417     /**
48418      * @cfg {String} method
48419      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48420      */
48421     /**
48422      * @cfg {DataReader} reader
48423      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48424      * This is optional as there is built-in support for processing JSON.
48425      */
48426     /**
48427      * @cfg {DataReader} errorReader
48428      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48429      * This is completely optional as there is built-in support for processing JSON.
48430      */
48431     /**
48432      * @cfg {String} url
48433      * The URL to use for form actions if one isn't supplied in the action options.
48434      */
48435     /**
48436      * @cfg {Boolean} fileUpload
48437      * Set to true if this form is a file upload.
48438      */
48439      
48440     /**
48441      * @cfg {Object} baseParams
48442      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48443      */
48444      /**
48445      
48446     /**
48447      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
48448      */
48449     timeout: 30,
48450
48451     // private
48452     activeAction : null,
48453
48454     /**
48455      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
48456      * or setValues() data instead of when the form was first created.
48457      */
48458     trackResetOnLoad : false,
48459     
48460     
48461     /**
48462      * childForms - used for multi-tab forms
48463      * @type {Array}
48464      */
48465     childForms : false,
48466     
48467     /**
48468      * allItems - full list of fields.
48469      * @type {Array}
48470      */
48471     allItems : false,
48472     
48473     /**
48474      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
48475      * element by passing it or its id or mask the form itself by passing in true.
48476      * @type Mixed
48477      */
48478     waitMsgTarget : false,
48479     
48480     /**
48481      * @type Boolean
48482      */
48483     disableMask : false,
48484     
48485     /**
48486      * @cfg {Boolean} errorMask (true|false) default false
48487      */
48488     errorMask : false,
48489     
48490     /**
48491      * @cfg {Number} maskOffset Default 100
48492      */
48493     maskOffset : 100,
48494
48495     // private
48496     initEl : function(el){
48497         this.el = Roo.get(el);
48498         this.id = this.el.id || Roo.id();
48499         this.el.on('submit', this.onSubmit, this);
48500         this.el.addClass('x-form');
48501     },
48502
48503     // private
48504     onSubmit : function(e){
48505         e.stopEvent();
48506     },
48507
48508     /**
48509      * Returns true if client-side validation on the form is successful.
48510      * @return Boolean
48511      */
48512     isValid : function(){
48513         var valid = true;
48514         var target = false;
48515         this.items.each(function(f){
48516             if(f.validate()){
48517                 return;
48518             }
48519             
48520             valid = false;
48521                 
48522             if(!target && f.el.isVisible(true)){
48523                 target = f;
48524             }
48525         });
48526         
48527         if(this.errorMask && !valid){
48528             Roo.form.BasicForm.popover.mask(this, target);
48529         }
48530         
48531         return valid;
48532     },
48533     /**
48534      * Returns array of invalid form fields.
48535      * @return Array
48536      */
48537     
48538     invalidFields : function()
48539     {
48540         var ret = [];
48541         this.items.each(function(f){
48542             if(f.validate()){
48543                 return;
48544             }
48545             ret.push(f);
48546             
48547         });
48548         
48549         return ret;
48550     },
48551     
48552     
48553     /**
48554      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
48555      * @return Boolean
48556      */
48557     isDirty : function(){
48558         var dirty = false;
48559         this.items.each(function(f){
48560            if(f.isDirty()){
48561                dirty = true;
48562                return false;
48563            }
48564         });
48565         return dirty;
48566     },
48567     
48568     /**
48569      * Returns true if any fields in this form have changed since their original load. (New version)
48570      * @return Boolean
48571      */
48572     
48573     hasChanged : function()
48574     {
48575         var dirty = false;
48576         this.items.each(function(f){
48577            if(f.hasChanged()){
48578                dirty = true;
48579                return false;
48580            }
48581         });
48582         return dirty;
48583         
48584     },
48585     /**
48586      * Resets all hasChanged to 'false' -
48587      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
48588      * So hasChanged storage is only to be used for this purpose
48589      * @return Boolean
48590      */
48591     resetHasChanged : function()
48592     {
48593         this.items.each(function(f){
48594            f.resetHasChanged();
48595         });
48596         
48597     },
48598     
48599     
48600     /**
48601      * Performs a predefined action (submit or load) or custom actions you define on this form.
48602      * @param {String} actionName The name of the action type
48603      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
48604      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
48605      * accept other config options):
48606      * <pre>
48607 Property          Type             Description
48608 ----------------  ---------------  ----------------------------------------------------------------------------------
48609 url               String           The url for the action (defaults to the form's url)
48610 method            String           The form method to use (defaults to the form's method, or POST if not defined)
48611 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
48612 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
48613                                    validate the form on the client (defaults to false)
48614      * </pre>
48615      * @return {BasicForm} this
48616      */
48617     doAction : function(action, options){
48618         if(typeof action == 'string'){
48619             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
48620         }
48621         if(this.fireEvent('beforeaction', this, action) !== false){
48622             this.beforeAction(action);
48623             action.run.defer(100, action);
48624         }
48625         return this;
48626     },
48627
48628     /**
48629      * Shortcut to do a submit action.
48630      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48631      * @return {BasicForm} this
48632      */
48633     submit : function(options){
48634         this.doAction('submit', options);
48635         return this;
48636     },
48637
48638     /**
48639      * Shortcut to do a load action.
48640      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48641      * @return {BasicForm} this
48642      */
48643     load : function(options){
48644         this.doAction('load', options);
48645         return this;
48646     },
48647
48648     /**
48649      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
48650      * @param {Record} record The record to edit
48651      * @return {BasicForm} this
48652      */
48653     updateRecord : function(record){
48654         record.beginEdit();
48655         var fs = record.fields;
48656         fs.each(function(f){
48657             var field = this.findField(f.name);
48658             if(field){
48659                 record.set(f.name, field.getValue());
48660             }
48661         }, this);
48662         record.endEdit();
48663         return this;
48664     },
48665
48666     /**
48667      * Loads an Roo.data.Record into this form.
48668      * @param {Record} record The record to load
48669      * @return {BasicForm} this
48670      */
48671     loadRecord : function(record){
48672         this.setValues(record.data);
48673         return this;
48674     },
48675
48676     // private
48677     beforeAction : function(action){
48678         var o = action.options;
48679         
48680         if(!this.disableMask) {
48681             if(this.waitMsgTarget === true){
48682                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
48683             }else if(this.waitMsgTarget){
48684                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
48685                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
48686             }else {
48687                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
48688             }
48689         }
48690         
48691          
48692     },
48693
48694     // private
48695     afterAction : function(action, success){
48696         this.activeAction = null;
48697         var o = action.options;
48698         
48699         if(!this.disableMask) {
48700             if(this.waitMsgTarget === true){
48701                 this.el.unmask();
48702             }else if(this.waitMsgTarget){
48703                 this.waitMsgTarget.unmask();
48704             }else{
48705                 Roo.MessageBox.updateProgress(1);
48706                 Roo.MessageBox.hide();
48707             }
48708         }
48709         
48710         if(success){
48711             if(o.reset){
48712                 this.reset();
48713             }
48714             Roo.callback(o.success, o.scope, [this, action]);
48715             this.fireEvent('actioncomplete', this, action);
48716             
48717         }else{
48718             
48719             // failure condition..
48720             // we have a scenario where updates need confirming.
48721             // eg. if a locking scenario exists..
48722             // we look for { errors : { needs_confirm : true }} in the response.
48723             if (
48724                 (typeof(action.result) != 'undefined')  &&
48725                 (typeof(action.result.errors) != 'undefined')  &&
48726                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48727            ){
48728                 var _t = this;
48729                 Roo.MessageBox.confirm(
48730                     "Change requires confirmation",
48731                     action.result.errorMsg,
48732                     function(r) {
48733                         if (r != 'yes') {
48734                             return;
48735                         }
48736                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48737                     }
48738                     
48739                 );
48740                 
48741                 
48742                 
48743                 return;
48744             }
48745             
48746             Roo.callback(o.failure, o.scope, [this, action]);
48747             // show an error message if no failed handler is set..
48748             if (!this.hasListener('actionfailed')) {
48749                 Roo.MessageBox.alert("Error",
48750                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48751                         action.result.errorMsg :
48752                         "Saving Failed, please check your entries or try again"
48753                 );
48754             }
48755             
48756             this.fireEvent('actionfailed', this, action);
48757         }
48758         
48759     },
48760
48761     /**
48762      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48763      * @param {String} id The value to search for
48764      * @return Field
48765      */
48766     findField : function(id){
48767         var field = this.items.get(id);
48768         if(!field){
48769             this.items.each(function(f){
48770                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48771                     field = f;
48772                     return false;
48773                 }
48774             });
48775         }
48776         return field || null;
48777     },
48778
48779     /**
48780      * Add a secondary form to this one, 
48781      * Used to provide tabbed forms. One form is primary, with hidden values 
48782      * which mirror the elements from the other forms.
48783      * 
48784      * @param {Roo.form.Form} form to add.
48785      * 
48786      */
48787     addForm : function(form)
48788     {
48789        
48790         if (this.childForms.indexOf(form) > -1) {
48791             // already added..
48792             return;
48793         }
48794         this.childForms.push(form);
48795         var n = '';
48796         Roo.each(form.allItems, function (fe) {
48797             
48798             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48799             if (this.findField(n)) { // already added..
48800                 return;
48801             }
48802             var add = new Roo.form.Hidden({
48803                 name : n
48804             });
48805             add.render(this.el);
48806             
48807             this.add( add );
48808         }, this);
48809         
48810     },
48811     /**
48812      * Mark fields in this form invalid in bulk.
48813      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48814      * @return {BasicForm} this
48815      */
48816     markInvalid : function(errors){
48817         if(errors instanceof Array){
48818             for(var i = 0, len = errors.length; i < len; i++){
48819                 var fieldError = errors[i];
48820                 var f = this.findField(fieldError.id);
48821                 if(f){
48822                     f.markInvalid(fieldError.msg);
48823                 }
48824             }
48825         }else{
48826             var field, id;
48827             for(id in errors){
48828                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48829                     field.markInvalid(errors[id]);
48830                 }
48831             }
48832         }
48833         Roo.each(this.childForms || [], function (f) {
48834             f.markInvalid(errors);
48835         });
48836         
48837         return this;
48838     },
48839
48840     /**
48841      * Set values for fields in this form in bulk.
48842      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48843      * @return {BasicForm} this
48844      */
48845     setValues : function(values){
48846         if(values instanceof Array){ // array of objects
48847             for(var i = 0, len = values.length; i < len; i++){
48848                 var v = values[i];
48849                 var f = this.findField(v.id);
48850                 if(f){
48851                     f.setValue(v.value);
48852                     if(this.trackResetOnLoad){
48853                         f.originalValue = f.getValue();
48854                     }
48855                 }
48856             }
48857         }else{ // object hash
48858             var field, id;
48859             for(id in values){
48860                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48861                     
48862                     if (field.setFromData && 
48863                         field.valueField && 
48864                         field.displayField &&
48865                         // combos' with local stores can 
48866                         // be queried via setValue()
48867                         // to set their value..
48868                         (field.store && !field.store.isLocal)
48869                         ) {
48870                         // it's a combo
48871                         var sd = { };
48872                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48873                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48874                         field.setFromData(sd);
48875                         
48876                     } else {
48877                         field.setValue(values[id]);
48878                     }
48879                     
48880                     
48881                     if(this.trackResetOnLoad){
48882                         field.originalValue = field.getValue();
48883                     }
48884                 }
48885             }
48886         }
48887         this.resetHasChanged();
48888         
48889         
48890         Roo.each(this.childForms || [], function (f) {
48891             f.setValues(values);
48892             f.resetHasChanged();
48893         });
48894                 
48895         return this;
48896     },
48897  
48898     /**
48899      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48900      * they are returned as an array.
48901      * @param {Boolean} asString
48902      * @return {Object}
48903      */
48904     getValues : function(asString){
48905         if (this.childForms) {
48906             // copy values from the child forms
48907             Roo.each(this.childForms, function (f) {
48908                 this.setValues(f.getValues());
48909             }, this);
48910         }
48911         
48912         // use formdata
48913         if (typeof(FormData) != 'undefined' && asString !== true) {
48914             // this relies on a 'recent' version of chrome apparently...
48915             try {
48916                 var fd = (new FormData(this.el.dom)).entries();
48917                 var ret = {};
48918                 var ent = fd.next();
48919                 while (!ent.done) {
48920                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48921                     ent = fd.next();
48922                 };
48923                 return ret;
48924             } catch(e) {
48925                 
48926             }
48927             
48928         }
48929         
48930         
48931         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48932         if(asString === true){
48933             return fs;
48934         }
48935         return Roo.urlDecode(fs);
48936     },
48937     
48938     /**
48939      * Returns the fields in this form as an object with key/value pairs. 
48940      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48941      * @return {Object}
48942      */
48943     getFieldValues : function(with_hidden)
48944     {
48945         if (this.childForms) {
48946             // copy values from the child forms
48947             // should this call getFieldValues - probably not as we do not currently copy
48948             // hidden fields when we generate..
48949             Roo.each(this.childForms, function (f) {
48950                 this.setValues(f.getValues());
48951             }, this);
48952         }
48953         
48954         var ret = {};
48955         this.items.each(function(f){
48956             if (!f.getName()) {
48957                 return;
48958             }
48959             var v = f.getValue();
48960             if (f.inputType =='radio') {
48961                 if (typeof(ret[f.getName()]) == 'undefined') {
48962                     ret[f.getName()] = ''; // empty..
48963                 }
48964                 
48965                 if (!f.el.dom.checked) {
48966                     return;
48967                     
48968                 }
48969                 v = f.el.dom.value;
48970                 
48971             }
48972             
48973             // not sure if this supported any more..
48974             if ((typeof(v) == 'object') && f.getRawValue) {
48975                 v = f.getRawValue() ; // dates..
48976             }
48977             // combo boxes where name != hiddenName...
48978             if (f.name != f.getName()) {
48979                 ret[f.name] = f.getRawValue();
48980             }
48981             ret[f.getName()] = v;
48982         });
48983         
48984         return ret;
48985     },
48986
48987     /**
48988      * Clears all invalid messages in this form.
48989      * @return {BasicForm} this
48990      */
48991     clearInvalid : function(){
48992         this.items.each(function(f){
48993            f.clearInvalid();
48994         });
48995         
48996         Roo.each(this.childForms || [], function (f) {
48997             f.clearInvalid();
48998         });
48999         
49000         
49001         return this;
49002     },
49003
49004     /**
49005      * Resets this form.
49006      * @return {BasicForm} this
49007      */
49008     reset : function(){
49009         this.items.each(function(f){
49010             f.reset();
49011         });
49012         
49013         Roo.each(this.childForms || [], function (f) {
49014             f.reset();
49015         });
49016         this.resetHasChanged();
49017         
49018         return this;
49019     },
49020
49021     /**
49022      * Add Roo.form components to this form.
49023      * @param {Field} field1
49024      * @param {Field} field2 (optional)
49025      * @param {Field} etc (optional)
49026      * @return {BasicForm} this
49027      */
49028     add : function(){
49029         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49030         return this;
49031     },
49032
49033
49034     /**
49035      * Removes a field from the items collection (does NOT remove its markup).
49036      * @param {Field} field
49037      * @return {BasicForm} this
49038      */
49039     remove : function(field){
49040         this.items.remove(field);
49041         return this;
49042     },
49043
49044     /**
49045      * Looks at the fields in this form, checks them for an id attribute,
49046      * and calls applyTo on the existing dom element with that id.
49047      * @return {BasicForm} this
49048      */
49049     render : function(){
49050         this.items.each(function(f){
49051             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49052                 f.applyTo(f.id);
49053             }
49054         });
49055         return this;
49056     },
49057
49058     /**
49059      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49060      * @param {Object} values
49061      * @return {BasicForm} this
49062      */
49063     applyToFields : function(o){
49064         this.items.each(function(f){
49065            Roo.apply(f, o);
49066         });
49067         return this;
49068     },
49069
49070     /**
49071      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49072      * @param {Object} values
49073      * @return {BasicForm} this
49074      */
49075     applyIfToFields : function(o){
49076         this.items.each(function(f){
49077            Roo.applyIf(f, o);
49078         });
49079         return this;
49080     }
49081 });
49082
49083 // back compat
49084 Roo.BasicForm = Roo.form.BasicForm;
49085
49086 Roo.apply(Roo.form.BasicForm, {
49087     
49088     popover : {
49089         
49090         padding : 5,
49091         
49092         isApplied : false,
49093         
49094         isMasked : false,
49095         
49096         form : false,
49097         
49098         target : false,
49099         
49100         intervalID : false,
49101         
49102         maskEl : false,
49103         
49104         apply : function()
49105         {
49106             if(this.isApplied){
49107                 return;
49108             }
49109             
49110             this.maskEl = {
49111                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49112                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49113                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49114                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49115             };
49116             
49117             this.maskEl.top.enableDisplayMode("block");
49118             this.maskEl.left.enableDisplayMode("block");
49119             this.maskEl.bottom.enableDisplayMode("block");
49120             this.maskEl.right.enableDisplayMode("block");
49121             
49122             Roo.get(document.body).on('click', function(){
49123                 this.unmask();
49124             }, this);
49125             
49126             Roo.get(document.body).on('touchstart', function(){
49127                 this.unmask();
49128             }, this);
49129             
49130             this.isApplied = true
49131         },
49132         
49133         mask : function(form, target)
49134         {
49135             this.form = form;
49136             
49137             this.target = target;
49138             
49139             if(!this.form.errorMask || !target.el){
49140                 return;
49141             }
49142             
49143             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49144             
49145             var ot = this.target.el.calcOffsetsTo(scrollable);
49146             
49147             var scrollTo = ot[1] - this.form.maskOffset;
49148             
49149             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49150             
49151             scrollable.scrollTo('top', scrollTo);
49152             
49153             var el = this.target.wrap || this.target.el;
49154             
49155             var box = el.getBox();
49156             
49157             this.maskEl.top.setStyle('position', 'absolute');
49158             this.maskEl.top.setStyle('z-index', 10000);
49159             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49160             this.maskEl.top.setLeft(0);
49161             this.maskEl.top.setTop(0);
49162             this.maskEl.top.show();
49163             
49164             this.maskEl.left.setStyle('position', 'absolute');
49165             this.maskEl.left.setStyle('z-index', 10000);
49166             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49167             this.maskEl.left.setLeft(0);
49168             this.maskEl.left.setTop(box.y - this.padding);
49169             this.maskEl.left.show();
49170
49171             this.maskEl.bottom.setStyle('position', 'absolute');
49172             this.maskEl.bottom.setStyle('z-index', 10000);
49173             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49174             this.maskEl.bottom.setLeft(0);
49175             this.maskEl.bottom.setTop(box.bottom + this.padding);
49176             this.maskEl.bottom.show();
49177
49178             this.maskEl.right.setStyle('position', 'absolute');
49179             this.maskEl.right.setStyle('z-index', 10000);
49180             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49181             this.maskEl.right.setLeft(box.right + this.padding);
49182             this.maskEl.right.setTop(box.y - this.padding);
49183             this.maskEl.right.show();
49184
49185             this.intervalID = window.setInterval(function() {
49186                 Roo.form.BasicForm.popover.unmask();
49187             }, 10000);
49188
49189             window.onwheel = function(){ return false;};
49190             
49191             (function(){ this.isMasked = true; }).defer(500, this);
49192             
49193         },
49194         
49195         unmask : function()
49196         {
49197             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49198                 return;
49199             }
49200             
49201             this.maskEl.top.setStyle('position', 'absolute');
49202             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49203             this.maskEl.top.hide();
49204
49205             this.maskEl.left.setStyle('position', 'absolute');
49206             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49207             this.maskEl.left.hide();
49208
49209             this.maskEl.bottom.setStyle('position', 'absolute');
49210             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49211             this.maskEl.bottom.hide();
49212
49213             this.maskEl.right.setStyle('position', 'absolute');
49214             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49215             this.maskEl.right.hide();
49216             
49217             window.onwheel = function(){ return true;};
49218             
49219             if(this.intervalID){
49220                 window.clearInterval(this.intervalID);
49221                 this.intervalID = false;
49222             }
49223             
49224             this.isMasked = false;
49225             
49226         }
49227         
49228     }
49229     
49230 });/*
49231  * Based on:
49232  * Ext JS Library 1.1.1
49233  * Copyright(c) 2006-2007, Ext JS, LLC.
49234  *
49235  * Originally Released Under LGPL - original licence link has changed is not relivant.
49236  *
49237  * Fork - LGPL
49238  * <script type="text/javascript">
49239  */
49240
49241 /**
49242  * @class Roo.form.Form
49243  * @extends Roo.form.BasicForm
49244  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49245  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49246  * @constructor
49247  * @param {Object} config Configuration options
49248  */
49249 Roo.form.Form = function(config){
49250     var xitems =  [];
49251     if (config.items) {
49252         xitems = config.items;
49253         delete config.items;
49254     }
49255    
49256     
49257     Roo.form.Form.superclass.constructor.call(this, null, config);
49258     this.url = this.url || this.action;
49259     if(!this.root){
49260         this.root = new Roo.form.Layout(Roo.applyIf({
49261             id: Roo.id()
49262         }, config));
49263     }
49264     this.active = this.root;
49265     /**
49266      * Array of all the buttons that have been added to this form via {@link addButton}
49267      * @type Array
49268      */
49269     this.buttons = [];
49270     this.allItems = [];
49271     this.addEvents({
49272         /**
49273          * @event clientvalidation
49274          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49275          * @param {Form} this
49276          * @param {Boolean} valid true if the form has passed client-side validation
49277          */
49278         clientvalidation: true,
49279         /**
49280          * @event rendered
49281          * Fires when the form is rendered
49282          * @param {Roo.form.Form} form
49283          */
49284         rendered : true
49285     });
49286     
49287     if (this.progressUrl) {
49288             // push a hidden field onto the list of fields..
49289             this.addxtype( {
49290                     xns: Roo.form, 
49291                     xtype : 'Hidden', 
49292                     name : 'UPLOAD_IDENTIFIER' 
49293             });
49294         }
49295         
49296     
49297     Roo.each(xitems, this.addxtype, this);
49298     
49299 };
49300
49301 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49302      /**
49303      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49304      */
49305     
49306     /**
49307      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49308      */
49309     /**
49310      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49311      */
49312     /**
49313      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49314      */
49315     buttonAlign:'center',
49316
49317     /**
49318      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49319      */
49320     minButtonWidth:75,
49321
49322     /**
49323      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49324      * This property cascades to child containers if not set.
49325      */
49326     labelAlign:'left',
49327
49328     /**
49329      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49330      * fires a looping event with that state. This is required to bind buttons to the valid
49331      * state using the config value formBind:true on the button.
49332      */
49333     monitorValid : false,
49334
49335     /**
49336      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49337      */
49338     monitorPoll : 200,
49339     
49340     /**
49341      * @cfg {String} progressUrl - Url to return progress data 
49342      */
49343     
49344     progressUrl : false,
49345     /**
49346      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49347      * sending a formdata with extra parameters - eg uploaded elements.
49348      */
49349     
49350     formData : false,
49351     
49352     /**
49353      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49354      * fields are added and the column is closed. If no fields are passed the column remains open
49355      * until end() is called.
49356      * @param {Object} config The config to pass to the column
49357      * @param {Field} field1 (optional)
49358      * @param {Field} field2 (optional)
49359      * @param {Field} etc (optional)
49360      * @return Column The column container object
49361      */
49362     column : function(c){
49363         var col = new Roo.form.Column(c);
49364         this.start(col);
49365         if(arguments.length > 1){ // duplicate code required because of Opera
49366             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49367             this.end();
49368         }
49369         return col;
49370     },
49371
49372     /**
49373      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49374      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49375      * until end() is called.
49376      * @param {Object} config The config to pass to the fieldset
49377      * @param {Field} field1 (optional)
49378      * @param {Field} field2 (optional)
49379      * @param {Field} etc (optional)
49380      * @return FieldSet The fieldset container object
49381      */
49382     fieldset : function(c){
49383         var fs = new Roo.form.FieldSet(c);
49384         this.start(fs);
49385         if(arguments.length > 1){ // duplicate code required because of Opera
49386             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49387             this.end();
49388         }
49389         return fs;
49390     },
49391
49392     /**
49393      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49394      * fields are added and the container is closed. If no fields are passed the container remains open
49395      * until end() is called.
49396      * @param {Object} config The config to pass to the Layout
49397      * @param {Field} field1 (optional)
49398      * @param {Field} field2 (optional)
49399      * @param {Field} etc (optional)
49400      * @return Layout The container object
49401      */
49402     container : function(c){
49403         var l = new Roo.form.Layout(c);
49404         this.start(l);
49405         if(arguments.length > 1){ // duplicate code required because of Opera
49406             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49407             this.end();
49408         }
49409         return l;
49410     },
49411
49412     /**
49413      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49414      * @param {Object} container A Roo.form.Layout or subclass of Layout
49415      * @return {Form} this
49416      */
49417     start : function(c){
49418         // cascade label info
49419         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49420         this.active.stack.push(c);
49421         c.ownerCt = this.active;
49422         this.active = c;
49423         return this;
49424     },
49425
49426     /**
49427      * Closes the current open container
49428      * @return {Form} this
49429      */
49430     end : function(){
49431         if(this.active == this.root){
49432             return this;
49433         }
49434         this.active = this.active.ownerCt;
49435         return this;
49436     },
49437
49438     /**
49439      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49440      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49441      * as the label of the field.
49442      * @param {Field} field1
49443      * @param {Field} field2 (optional)
49444      * @param {Field} etc. (optional)
49445      * @return {Form} this
49446      */
49447     add : function(){
49448         this.active.stack.push.apply(this.active.stack, arguments);
49449         this.allItems.push.apply(this.allItems,arguments);
49450         var r = [];
49451         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
49452             if(a[i].isFormField){
49453                 r.push(a[i]);
49454             }
49455         }
49456         if(r.length > 0){
49457             Roo.form.Form.superclass.add.apply(this, r);
49458         }
49459         return this;
49460     },
49461     
49462
49463     
49464     
49465     
49466      /**
49467      * Find any element that has been added to a form, using it's ID or name
49468      * This can include framesets, columns etc. along with regular fields..
49469      * @param {String} id - id or name to find.
49470      
49471      * @return {Element} e - or false if nothing found.
49472      */
49473     findbyId : function(id)
49474     {
49475         var ret = false;
49476         if (!id) {
49477             return ret;
49478         }
49479         Roo.each(this.allItems, function(f){
49480             if (f.id == id || f.name == id ){
49481                 ret = f;
49482                 return false;
49483             }
49484         });
49485         return ret;
49486     },
49487
49488     
49489     
49490     /**
49491      * Render this form into the passed container. This should only be called once!
49492      * @param {String/HTMLElement/Element} container The element this component should be rendered into
49493      * @return {Form} this
49494      */
49495     render : function(ct)
49496     {
49497         
49498         
49499         
49500         ct = Roo.get(ct);
49501         var o = this.autoCreate || {
49502             tag: 'form',
49503             method : this.method || 'POST',
49504             id : this.id || Roo.id()
49505         };
49506         this.initEl(ct.createChild(o));
49507
49508         this.root.render(this.el);
49509         
49510        
49511              
49512         this.items.each(function(f){
49513             f.render('x-form-el-'+f.id);
49514         });
49515
49516         if(this.buttons.length > 0){
49517             // tables are required to maintain order and for correct IE layout
49518             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
49519                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
49520                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
49521             }}, null, true);
49522             var tr = tb.getElementsByTagName('tr')[0];
49523             for(var i = 0, len = this.buttons.length; i < len; i++) {
49524                 var b = this.buttons[i];
49525                 var td = document.createElement('td');
49526                 td.className = 'x-form-btn-td';
49527                 b.render(tr.appendChild(td));
49528             }
49529         }
49530         if(this.monitorValid){ // initialize after render
49531             this.startMonitoring();
49532         }
49533         this.fireEvent('rendered', this);
49534         return this;
49535     },
49536
49537     /**
49538      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
49539      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
49540      * object or a valid Roo.DomHelper element config
49541      * @param {Function} handler The function called when the button is clicked
49542      * @param {Object} scope (optional) The scope of the handler function
49543      * @return {Roo.Button}
49544      */
49545     addButton : function(config, handler, scope){
49546         var bc = {
49547             handler: handler,
49548             scope: scope,
49549             minWidth: this.minButtonWidth,
49550             hideParent:true
49551         };
49552         if(typeof config == "string"){
49553             bc.text = config;
49554         }else{
49555             Roo.apply(bc, config);
49556         }
49557         var btn = new Roo.Button(null, bc);
49558         this.buttons.push(btn);
49559         return btn;
49560     },
49561
49562      /**
49563      * Adds a series of form elements (using the xtype property as the factory method.
49564      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
49565      * @param {Object} config 
49566      */
49567     
49568     addxtype : function()
49569     {
49570         var ar = Array.prototype.slice.call(arguments, 0);
49571         var ret = false;
49572         for(var i = 0; i < ar.length; i++) {
49573             if (!ar[i]) {
49574                 continue; // skip -- if this happends something invalid got sent, we 
49575                 // should ignore it, as basically that interface element will not show up
49576                 // and that should be pretty obvious!!
49577             }
49578             
49579             if (Roo.form[ar[i].xtype]) {
49580                 ar[i].form = this;
49581                 var fe = Roo.factory(ar[i], Roo.form);
49582                 if (!ret) {
49583                     ret = fe;
49584                 }
49585                 fe.form = this;
49586                 if (fe.store) {
49587                     fe.store.form = this;
49588                 }
49589                 if (fe.isLayout) {  
49590                          
49591                     this.start(fe);
49592                     this.allItems.push(fe);
49593                     if (fe.items && fe.addxtype) {
49594                         fe.addxtype.apply(fe, fe.items);
49595                         delete fe.items;
49596                     }
49597                      this.end();
49598                     continue;
49599                 }
49600                 
49601                 
49602                  
49603                 this.add(fe);
49604               //  console.log('adding ' + ar[i].xtype);
49605             }
49606             if (ar[i].xtype == 'Button') {  
49607                 //console.log('adding button');
49608                 //console.log(ar[i]);
49609                 this.addButton(ar[i]);
49610                 this.allItems.push(fe);
49611                 continue;
49612             }
49613             
49614             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
49615                 alert('end is not supported on xtype any more, use items');
49616             //    this.end();
49617             //    //console.log('adding end');
49618             }
49619             
49620         }
49621         return ret;
49622     },
49623     
49624     /**
49625      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
49626      * option "monitorValid"
49627      */
49628     startMonitoring : function(){
49629         if(!this.bound){
49630             this.bound = true;
49631             Roo.TaskMgr.start({
49632                 run : this.bindHandler,
49633                 interval : this.monitorPoll || 200,
49634                 scope: this
49635             });
49636         }
49637     },
49638
49639     /**
49640      * Stops monitoring of the valid state of this form
49641      */
49642     stopMonitoring : function(){
49643         this.bound = false;
49644     },
49645
49646     // private
49647     bindHandler : function(){
49648         if(!this.bound){
49649             return false; // stops binding
49650         }
49651         var valid = true;
49652         this.items.each(function(f){
49653             if(!f.isValid(true)){
49654                 valid = false;
49655                 return false;
49656             }
49657         });
49658         for(var i = 0, len = this.buttons.length; i < len; i++){
49659             var btn = this.buttons[i];
49660             if(btn.formBind === true && btn.disabled === valid){
49661                 btn.setDisabled(!valid);
49662             }
49663         }
49664         this.fireEvent('clientvalidation', this, valid);
49665     }
49666     
49667     
49668     
49669     
49670     
49671     
49672     
49673     
49674 });
49675
49676
49677 // back compat
49678 Roo.Form = Roo.form.Form;
49679 /*
49680  * Based on:
49681  * Ext JS Library 1.1.1
49682  * Copyright(c) 2006-2007, Ext JS, LLC.
49683  *
49684  * Originally Released Under LGPL - original licence link has changed is not relivant.
49685  *
49686  * Fork - LGPL
49687  * <script type="text/javascript">
49688  */
49689
49690 // as we use this in bootstrap.
49691 Roo.namespace('Roo.form');
49692  /**
49693  * @class Roo.form.Action
49694  * Internal Class used to handle form actions
49695  * @constructor
49696  * @param {Roo.form.BasicForm} el The form element or its id
49697  * @param {Object} config Configuration options
49698  */
49699
49700  
49701  
49702 // define the action interface
49703 Roo.form.Action = function(form, options){
49704     this.form = form;
49705     this.options = options || {};
49706 };
49707 /**
49708  * Client Validation Failed
49709  * @const 
49710  */
49711 Roo.form.Action.CLIENT_INVALID = 'client';
49712 /**
49713  * Server Validation Failed
49714  * @const 
49715  */
49716 Roo.form.Action.SERVER_INVALID = 'server';
49717  /**
49718  * Connect to Server Failed
49719  * @const 
49720  */
49721 Roo.form.Action.CONNECT_FAILURE = 'connect';
49722 /**
49723  * Reading Data from Server Failed
49724  * @const 
49725  */
49726 Roo.form.Action.LOAD_FAILURE = 'load';
49727
49728 Roo.form.Action.prototype = {
49729     type : 'default',
49730     failureType : undefined,
49731     response : undefined,
49732     result : undefined,
49733
49734     // interface method
49735     run : function(options){
49736
49737     },
49738
49739     // interface method
49740     success : function(response){
49741
49742     },
49743
49744     // interface method
49745     handleResponse : function(response){
49746
49747     },
49748
49749     // default connection failure
49750     failure : function(response){
49751         
49752         this.response = response;
49753         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49754         this.form.afterAction(this, false);
49755     },
49756
49757     processResponse : function(response){
49758         this.response = response;
49759         if(!response.responseText){
49760             return true;
49761         }
49762         this.result = this.handleResponse(response);
49763         return this.result;
49764     },
49765
49766     // utility functions used internally
49767     getUrl : function(appendParams){
49768         var url = this.options.url || this.form.url || this.form.el.dom.action;
49769         if(appendParams){
49770             var p = this.getParams();
49771             if(p){
49772                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49773             }
49774         }
49775         return url;
49776     },
49777
49778     getMethod : function(){
49779         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49780     },
49781
49782     getParams : function(){
49783         var bp = this.form.baseParams;
49784         var p = this.options.params;
49785         if(p){
49786             if(typeof p == "object"){
49787                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49788             }else if(typeof p == 'string' && bp){
49789                 p += '&' + Roo.urlEncode(bp);
49790             }
49791         }else if(bp){
49792             p = Roo.urlEncode(bp);
49793         }
49794         return p;
49795     },
49796
49797     createCallback : function(){
49798         return {
49799             success: this.success,
49800             failure: this.failure,
49801             scope: this,
49802             timeout: (this.form.timeout*1000),
49803             upload: this.form.fileUpload ? this.success : undefined
49804         };
49805     }
49806 };
49807
49808 Roo.form.Action.Submit = function(form, options){
49809     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49810 };
49811
49812 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49813     type : 'submit',
49814
49815     haveProgress : false,
49816     uploadComplete : false,
49817     
49818     // uploadProgress indicator.
49819     uploadProgress : function()
49820     {
49821         if (!this.form.progressUrl) {
49822             return;
49823         }
49824         
49825         if (!this.haveProgress) {
49826             Roo.MessageBox.progress("Uploading", "Uploading");
49827         }
49828         if (this.uploadComplete) {
49829            Roo.MessageBox.hide();
49830            return;
49831         }
49832         
49833         this.haveProgress = true;
49834    
49835         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49836         
49837         var c = new Roo.data.Connection();
49838         c.request({
49839             url : this.form.progressUrl,
49840             params: {
49841                 id : uid
49842             },
49843             method: 'GET',
49844             success : function(req){
49845                //console.log(data);
49846                 var rdata = false;
49847                 var edata;
49848                 try  {
49849                    rdata = Roo.decode(req.responseText)
49850                 } catch (e) {
49851                     Roo.log("Invalid data from server..");
49852                     Roo.log(edata);
49853                     return;
49854                 }
49855                 if (!rdata || !rdata.success) {
49856                     Roo.log(rdata);
49857                     Roo.MessageBox.alert(Roo.encode(rdata));
49858                     return;
49859                 }
49860                 var data = rdata.data;
49861                 
49862                 if (this.uploadComplete) {
49863                    Roo.MessageBox.hide();
49864                    return;
49865                 }
49866                    
49867                 if (data){
49868                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49869                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49870                     );
49871                 }
49872                 this.uploadProgress.defer(2000,this);
49873             },
49874        
49875             failure: function(data) {
49876                 Roo.log('progress url failed ');
49877                 Roo.log(data);
49878             },
49879             scope : this
49880         });
49881            
49882     },
49883     
49884     
49885     run : function()
49886     {
49887         // run get Values on the form, so it syncs any secondary forms.
49888         this.form.getValues();
49889         
49890         var o = this.options;
49891         var method = this.getMethod();
49892         var isPost = method == 'POST';
49893         if(o.clientValidation === false || this.form.isValid()){
49894             
49895             if (this.form.progressUrl) {
49896                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49897                     (new Date() * 1) + '' + Math.random());
49898                     
49899             } 
49900             
49901             
49902             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49903                 form:this.form.el.dom,
49904                 url:this.getUrl(!isPost),
49905                 method: method,
49906                 params:isPost ? this.getParams() : null,
49907                 isUpload: this.form.fileUpload,
49908                 formData : this.form.formData
49909             }));
49910             
49911             this.uploadProgress();
49912
49913         }else if (o.clientValidation !== false){ // client validation failed
49914             this.failureType = Roo.form.Action.CLIENT_INVALID;
49915             this.form.afterAction(this, false);
49916         }
49917     },
49918
49919     success : function(response)
49920     {
49921         this.uploadComplete= true;
49922         if (this.haveProgress) {
49923             Roo.MessageBox.hide();
49924         }
49925         
49926         
49927         var result = this.processResponse(response);
49928         if(result === true || result.success){
49929             this.form.afterAction(this, true);
49930             return;
49931         }
49932         if(result.errors){
49933             this.form.markInvalid(result.errors);
49934             this.failureType = Roo.form.Action.SERVER_INVALID;
49935         }
49936         this.form.afterAction(this, false);
49937     },
49938     failure : function(response)
49939     {
49940         this.uploadComplete= true;
49941         if (this.haveProgress) {
49942             Roo.MessageBox.hide();
49943         }
49944         
49945         this.response = response;
49946         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49947         this.form.afterAction(this, false);
49948     },
49949     
49950     handleResponse : function(response){
49951         if(this.form.errorReader){
49952             var rs = this.form.errorReader.read(response);
49953             var errors = [];
49954             if(rs.records){
49955                 for(var i = 0, len = rs.records.length; i < len; i++) {
49956                     var r = rs.records[i];
49957                     errors[i] = r.data;
49958                 }
49959             }
49960             if(errors.length < 1){
49961                 errors = null;
49962             }
49963             return {
49964                 success : rs.success,
49965                 errors : errors
49966             };
49967         }
49968         var ret = false;
49969         try {
49970             ret = Roo.decode(response.responseText);
49971         } catch (e) {
49972             ret = {
49973                 success: false,
49974                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49975                 errors : []
49976             };
49977         }
49978         return ret;
49979         
49980     }
49981 });
49982
49983
49984 Roo.form.Action.Load = function(form, options){
49985     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49986     this.reader = this.form.reader;
49987 };
49988
49989 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49990     type : 'load',
49991
49992     run : function(){
49993         
49994         Roo.Ajax.request(Roo.apply(
49995                 this.createCallback(), {
49996                     method:this.getMethod(),
49997                     url:this.getUrl(false),
49998                     params:this.getParams()
49999         }));
50000     },
50001
50002     success : function(response){
50003         
50004         var result = this.processResponse(response);
50005         if(result === true || !result.success || !result.data){
50006             this.failureType = Roo.form.Action.LOAD_FAILURE;
50007             this.form.afterAction(this, false);
50008             return;
50009         }
50010         this.form.clearInvalid();
50011         this.form.setValues(result.data);
50012         this.form.afterAction(this, true);
50013     },
50014
50015     handleResponse : function(response){
50016         if(this.form.reader){
50017             var rs = this.form.reader.read(response);
50018             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50019             return {
50020                 success : rs.success,
50021                 data : data
50022             };
50023         }
50024         return Roo.decode(response.responseText);
50025     }
50026 });
50027
50028 Roo.form.Action.ACTION_TYPES = {
50029     'load' : Roo.form.Action.Load,
50030     'submit' : Roo.form.Action.Submit
50031 };/*
50032  * Based on:
50033  * Ext JS Library 1.1.1
50034  * Copyright(c) 2006-2007, Ext JS, LLC.
50035  *
50036  * Originally Released Under LGPL - original licence link has changed is not relivant.
50037  *
50038  * Fork - LGPL
50039  * <script type="text/javascript">
50040  */
50041  
50042 /**
50043  * @class Roo.form.Layout
50044  * @extends Roo.Component
50045  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50046  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50047  * @constructor
50048  * @param {Object} config Configuration options
50049  */
50050 Roo.form.Layout = function(config){
50051     var xitems = [];
50052     if (config.items) {
50053         xitems = config.items;
50054         delete config.items;
50055     }
50056     Roo.form.Layout.superclass.constructor.call(this, config);
50057     this.stack = [];
50058     Roo.each(xitems, this.addxtype, this);
50059      
50060 };
50061
50062 Roo.extend(Roo.form.Layout, Roo.Component, {
50063     /**
50064      * @cfg {String/Object} autoCreate
50065      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50066      */
50067     /**
50068      * @cfg {String/Object/Function} style
50069      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50070      * a function which returns such a specification.
50071      */
50072     /**
50073      * @cfg {String} labelAlign
50074      * Valid values are "left," "top" and "right" (defaults to "left")
50075      */
50076     /**
50077      * @cfg {Number} labelWidth
50078      * Fixed width in pixels of all field labels (defaults to undefined)
50079      */
50080     /**
50081      * @cfg {Boolean} clear
50082      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50083      */
50084     clear : true,
50085     /**
50086      * @cfg {String} labelSeparator
50087      * The separator to use after field labels (defaults to ':')
50088      */
50089     labelSeparator : ':',
50090     /**
50091      * @cfg {Boolean} hideLabels
50092      * True to suppress the display of field labels in this layout (defaults to false)
50093      */
50094     hideLabels : false,
50095
50096     // private
50097     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50098     
50099     isLayout : true,
50100     
50101     // private
50102     onRender : function(ct, position){
50103         if(this.el){ // from markup
50104             this.el = Roo.get(this.el);
50105         }else {  // generate
50106             var cfg = this.getAutoCreate();
50107             this.el = ct.createChild(cfg, position);
50108         }
50109         if(this.style){
50110             this.el.applyStyles(this.style);
50111         }
50112         if(this.labelAlign){
50113             this.el.addClass('x-form-label-'+this.labelAlign);
50114         }
50115         if(this.hideLabels){
50116             this.labelStyle = "display:none";
50117             this.elementStyle = "padding-left:0;";
50118         }else{
50119             if(typeof this.labelWidth == 'number'){
50120                 this.labelStyle = "width:"+this.labelWidth+"px;";
50121                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50122             }
50123             if(this.labelAlign == 'top'){
50124                 this.labelStyle = "width:auto;";
50125                 this.elementStyle = "padding-left:0;";
50126             }
50127         }
50128         var stack = this.stack;
50129         var slen = stack.length;
50130         if(slen > 0){
50131             if(!this.fieldTpl){
50132                 var t = new Roo.Template(
50133                     '<div class="x-form-item {5}">',
50134                         '<label for="{0}" style="{2}">{1}{4}</label>',
50135                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50136                         '</div>',
50137                     '</div><div class="x-form-clear-left"></div>'
50138                 );
50139                 t.disableFormats = true;
50140                 t.compile();
50141                 Roo.form.Layout.prototype.fieldTpl = t;
50142             }
50143             for(var i = 0; i < slen; i++) {
50144                 if(stack[i].isFormField){
50145                     this.renderField(stack[i]);
50146                 }else{
50147                     this.renderComponent(stack[i]);
50148                 }
50149             }
50150         }
50151         if(this.clear){
50152             this.el.createChild({cls:'x-form-clear'});
50153         }
50154     },
50155
50156     // private
50157     renderField : function(f){
50158         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50159                f.id, //0
50160                f.fieldLabel, //1
50161                f.labelStyle||this.labelStyle||'', //2
50162                this.elementStyle||'', //3
50163                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50164                f.itemCls||this.itemCls||''  //5
50165        ], true).getPrevSibling());
50166     },
50167
50168     // private
50169     renderComponent : function(c){
50170         c.render(c.isLayout ? this.el : this.el.createChild());    
50171     },
50172     /**
50173      * Adds a object form elements (using the xtype property as the factory method.)
50174      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50175      * @param {Object} config 
50176      */
50177     addxtype : function(o)
50178     {
50179         // create the lement.
50180         o.form = this.form;
50181         var fe = Roo.factory(o, Roo.form);
50182         this.form.allItems.push(fe);
50183         this.stack.push(fe);
50184         
50185         if (fe.isFormField) {
50186             this.form.items.add(fe);
50187         }
50188          
50189         return fe;
50190     }
50191 });
50192
50193 /**
50194  * @class Roo.form.Column
50195  * @extends Roo.form.Layout
50196  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50197  * @constructor
50198  * @param {Object} config Configuration options
50199  */
50200 Roo.form.Column = function(config){
50201     Roo.form.Column.superclass.constructor.call(this, config);
50202 };
50203
50204 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50205     /**
50206      * @cfg {Number/String} width
50207      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50208      */
50209     /**
50210      * @cfg {String/Object} autoCreate
50211      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50212      */
50213
50214     // private
50215     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50216
50217     // private
50218     onRender : function(ct, position){
50219         Roo.form.Column.superclass.onRender.call(this, ct, position);
50220         if(this.width){
50221             this.el.setWidth(this.width);
50222         }
50223     }
50224 });
50225
50226
50227 /**
50228  * @class Roo.form.Row
50229  * @extends Roo.form.Layout
50230  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50231  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50232  * @constructor
50233  * @param {Object} config Configuration options
50234  */
50235
50236  
50237 Roo.form.Row = function(config){
50238     Roo.form.Row.superclass.constructor.call(this, config);
50239 };
50240  
50241 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50242       /**
50243      * @cfg {Number/String} width
50244      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50245      */
50246     /**
50247      * @cfg {Number/String} height
50248      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50249      */
50250     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50251     
50252     padWidth : 20,
50253     // private
50254     onRender : function(ct, position){
50255         //console.log('row render');
50256         if(!this.rowTpl){
50257             var t = new Roo.Template(
50258                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50259                     '<label for="{0}" style="{2}">{1}{4}</label>',
50260                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50261                     '</div>',
50262                 '</div>'
50263             );
50264             t.disableFormats = true;
50265             t.compile();
50266             Roo.form.Layout.prototype.rowTpl = t;
50267         }
50268         this.fieldTpl = this.rowTpl;
50269         
50270         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50271         var labelWidth = 100;
50272         
50273         if ((this.labelAlign != 'top')) {
50274             if (typeof this.labelWidth == 'number') {
50275                 labelWidth = this.labelWidth
50276             }
50277             this.padWidth =  20 + labelWidth;
50278             
50279         }
50280         
50281         Roo.form.Column.superclass.onRender.call(this, ct, position);
50282         if(this.width){
50283             this.el.setWidth(this.width);
50284         }
50285         if(this.height){
50286             this.el.setHeight(this.height);
50287         }
50288     },
50289     
50290     // private
50291     renderField : function(f){
50292         f.fieldEl = this.fieldTpl.append(this.el, [
50293                f.id, f.fieldLabel,
50294                f.labelStyle||this.labelStyle||'',
50295                this.elementStyle||'',
50296                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50297                f.itemCls||this.itemCls||'',
50298                f.width ? f.width + this.padWidth : 160 + this.padWidth
50299        ],true);
50300     }
50301 });
50302  
50303
50304 /**
50305  * @class Roo.form.FieldSet
50306  * @extends Roo.form.Layout
50307  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50308  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50309  * @constructor
50310  * @param {Object} config Configuration options
50311  */
50312 Roo.form.FieldSet = function(config){
50313     Roo.form.FieldSet.superclass.constructor.call(this, config);
50314 };
50315
50316 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50317     /**
50318      * @cfg {String} legend
50319      * The text to display as the legend for the FieldSet (defaults to '')
50320      */
50321     /**
50322      * @cfg {String/Object} autoCreate
50323      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50324      */
50325
50326     // private
50327     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50328
50329     // private
50330     onRender : function(ct, position){
50331         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50332         if(this.legend){
50333             this.setLegend(this.legend);
50334         }
50335     },
50336
50337     // private
50338     setLegend : function(text){
50339         if(this.rendered){
50340             this.el.child('legend').update(text);
50341         }
50342     }
50343 });/*
50344  * Based on:
50345  * Ext JS Library 1.1.1
50346  * Copyright(c) 2006-2007, Ext JS, LLC.
50347  *
50348  * Originally Released Under LGPL - original licence link has changed is not relivant.
50349  *
50350  * Fork - LGPL
50351  * <script type="text/javascript">
50352  */
50353 /**
50354  * @class Roo.form.VTypes
50355  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50356  * @static
50357  */
50358 Roo.form.VTypes = function(){
50359     // closure these in so they are only created once.
50360     var alpha = /^[a-zA-Z_]+$/;
50361     var alphanum = /^[a-zA-Z0-9_]+$/;
50362     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50363     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50364
50365     // All these messages and functions are configurable
50366     return {
50367         /**
50368          * The function used to validate email addresses
50369          * @param {String} value The email address
50370          */
50371         'email' : function(v){
50372             return email.test(v);
50373         },
50374         /**
50375          * The error text to display when the email validation function returns false
50376          * @type String
50377          */
50378         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50379         /**
50380          * The keystroke filter mask to be applied on email input
50381          * @type RegExp
50382          */
50383         'emailMask' : /[a-z0-9_\.\-@]/i,
50384
50385         /**
50386          * The function used to validate URLs
50387          * @param {String} value The URL
50388          */
50389         'url' : function(v){
50390             return url.test(v);
50391         },
50392         /**
50393          * The error text to display when the url validation function returns false
50394          * @type String
50395          */
50396         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50397         
50398         /**
50399          * The function used to validate alpha values
50400          * @param {String} value The value
50401          */
50402         'alpha' : function(v){
50403             return alpha.test(v);
50404         },
50405         /**
50406          * The error text to display when the alpha validation function returns false
50407          * @type String
50408          */
50409         'alphaText' : 'This field should only contain letters and _',
50410         /**
50411          * The keystroke filter mask to be applied on alpha input
50412          * @type RegExp
50413          */
50414         'alphaMask' : /[a-z_]/i,
50415
50416         /**
50417          * The function used to validate alphanumeric values
50418          * @param {String} value The value
50419          */
50420         'alphanum' : function(v){
50421             return alphanum.test(v);
50422         },
50423         /**
50424          * The error text to display when the alphanumeric validation function returns false
50425          * @type String
50426          */
50427         'alphanumText' : 'This field should only contain letters, numbers and _',
50428         /**
50429          * The keystroke filter mask to be applied on alphanumeric input
50430          * @type RegExp
50431          */
50432         'alphanumMask' : /[a-z0-9_]/i
50433     };
50434 }();//<script type="text/javascript">
50435
50436 /**
50437  * @class Roo.form.FCKeditor
50438  * @extends Roo.form.TextArea
50439  * Wrapper around the FCKEditor http://www.fckeditor.net
50440  * @constructor
50441  * Creates a new FCKeditor
50442  * @param {Object} config Configuration options
50443  */
50444 Roo.form.FCKeditor = function(config){
50445     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50446     this.addEvents({
50447          /**
50448          * @event editorinit
50449          * Fired when the editor is initialized - you can add extra handlers here..
50450          * @param {FCKeditor} this
50451          * @param {Object} the FCK object.
50452          */
50453         editorinit : true
50454     });
50455     
50456     
50457 };
50458 Roo.form.FCKeditor.editors = { };
50459 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
50460 {
50461     //defaultAutoCreate : {
50462     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
50463     //},
50464     // private
50465     /**
50466      * @cfg {Object} fck options - see fck manual for details.
50467      */
50468     fckconfig : false,
50469     
50470     /**
50471      * @cfg {Object} fck toolbar set (Basic or Default)
50472      */
50473     toolbarSet : 'Basic',
50474     /**
50475      * @cfg {Object} fck BasePath
50476      */ 
50477     basePath : '/fckeditor/',
50478     
50479     
50480     frame : false,
50481     
50482     value : '',
50483     
50484    
50485     onRender : function(ct, position)
50486     {
50487         if(!this.el){
50488             this.defaultAutoCreate = {
50489                 tag: "textarea",
50490                 style:"width:300px;height:60px;",
50491                 autocomplete: "new-password"
50492             };
50493         }
50494         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
50495         /*
50496         if(this.grow){
50497             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
50498             if(this.preventScrollbars){
50499                 this.el.setStyle("overflow", "hidden");
50500             }
50501             this.el.setHeight(this.growMin);
50502         }
50503         */
50504         //console.log('onrender' + this.getId() );
50505         Roo.form.FCKeditor.editors[this.getId()] = this;
50506          
50507
50508         this.replaceTextarea() ;
50509         
50510     },
50511     
50512     getEditor : function() {
50513         return this.fckEditor;
50514     },
50515     /**
50516      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
50517      * @param {Mixed} value The value to set
50518      */
50519     
50520     
50521     setValue : function(value)
50522     {
50523         //console.log('setValue: ' + value);
50524         
50525         if(typeof(value) == 'undefined') { // not sure why this is happending...
50526             return;
50527         }
50528         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50529         
50530         //if(!this.el || !this.getEditor()) {
50531         //    this.value = value;
50532             //this.setValue.defer(100,this,[value]);    
50533         //    return;
50534         //} 
50535         
50536         if(!this.getEditor()) {
50537             return;
50538         }
50539         
50540         this.getEditor().SetData(value);
50541         
50542         //
50543
50544     },
50545
50546     /**
50547      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
50548      * @return {Mixed} value The field value
50549      */
50550     getValue : function()
50551     {
50552         
50553         if (this.frame && this.frame.dom.style.display == 'none') {
50554             return Roo.form.FCKeditor.superclass.getValue.call(this);
50555         }
50556         
50557         if(!this.el || !this.getEditor()) {
50558            
50559            // this.getValue.defer(100,this); 
50560             return this.value;
50561         }
50562        
50563         
50564         var value=this.getEditor().GetData();
50565         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50566         return Roo.form.FCKeditor.superclass.getValue.call(this);
50567         
50568
50569     },
50570
50571     /**
50572      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
50573      * @return {Mixed} value The field value
50574      */
50575     getRawValue : function()
50576     {
50577         if (this.frame && this.frame.dom.style.display == 'none') {
50578             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50579         }
50580         
50581         if(!this.el || !this.getEditor()) {
50582             //this.getRawValue.defer(100,this); 
50583             return this.value;
50584             return;
50585         }
50586         
50587         
50588         
50589         var value=this.getEditor().GetData();
50590         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
50591         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50592          
50593     },
50594     
50595     setSize : function(w,h) {
50596         
50597         
50598         
50599         //if (this.frame && this.frame.dom.style.display == 'none') {
50600         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50601         //    return;
50602         //}
50603         //if(!this.el || !this.getEditor()) {
50604         //    this.setSize.defer(100,this, [w,h]); 
50605         //    return;
50606         //}
50607         
50608         
50609         
50610         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50611         
50612         this.frame.dom.setAttribute('width', w);
50613         this.frame.dom.setAttribute('height', h);
50614         this.frame.setSize(w,h);
50615         
50616     },
50617     
50618     toggleSourceEdit : function(value) {
50619         
50620       
50621          
50622         this.el.dom.style.display = value ? '' : 'none';
50623         this.frame.dom.style.display = value ?  'none' : '';
50624         
50625     },
50626     
50627     
50628     focus: function(tag)
50629     {
50630         if (this.frame.dom.style.display == 'none') {
50631             return Roo.form.FCKeditor.superclass.focus.call(this);
50632         }
50633         if(!this.el || !this.getEditor()) {
50634             this.focus.defer(100,this, [tag]); 
50635             return;
50636         }
50637         
50638         
50639         
50640         
50641         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
50642         this.getEditor().Focus();
50643         if (tgs.length) {
50644             if (!this.getEditor().Selection.GetSelection()) {
50645                 this.focus.defer(100,this, [tag]); 
50646                 return;
50647             }
50648             
50649             
50650             var r = this.getEditor().EditorDocument.createRange();
50651             r.setStart(tgs[0],0);
50652             r.setEnd(tgs[0],0);
50653             this.getEditor().Selection.GetSelection().removeAllRanges();
50654             this.getEditor().Selection.GetSelection().addRange(r);
50655             this.getEditor().Focus();
50656         }
50657         
50658     },
50659     
50660     
50661     
50662     replaceTextarea : function()
50663     {
50664         if ( document.getElementById( this.getId() + '___Frame' ) ) {
50665             return ;
50666         }
50667         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
50668         //{
50669             // We must check the elements firstly using the Id and then the name.
50670         var oTextarea = document.getElementById( this.getId() );
50671         
50672         var colElementsByName = document.getElementsByName( this.getId() ) ;
50673          
50674         oTextarea.style.display = 'none' ;
50675
50676         if ( oTextarea.tabIndex ) {            
50677             this.TabIndex = oTextarea.tabIndex ;
50678         }
50679         
50680         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
50681         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
50682         this.frame = Roo.get(this.getId() + '___Frame')
50683     },
50684     
50685     _getConfigHtml : function()
50686     {
50687         var sConfig = '' ;
50688
50689         for ( var o in this.fckconfig ) {
50690             sConfig += sConfig.length > 0  ? '&amp;' : '';
50691             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
50692         }
50693
50694         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
50695     },
50696     
50697     
50698     _getIFrameHtml : function()
50699     {
50700         var sFile = 'fckeditor.html' ;
50701         /* no idea what this is about..
50702         try
50703         {
50704             if ( (/fcksource=true/i).test( window.top.location.search ) )
50705                 sFile = 'fckeditor.original.html' ;
50706         }
50707         catch (e) { 
50708         */
50709
50710         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
50711         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
50712         
50713         
50714         var html = '<iframe id="' + this.getId() +
50715             '___Frame" src="' + sLink +
50716             '" width="' + this.width +
50717             '" height="' + this.height + '"' +
50718             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
50719             ' frameborder="0" scrolling="no"></iframe>' ;
50720
50721         return html ;
50722     },
50723     
50724     _insertHtmlBefore : function( html, element )
50725     {
50726         if ( element.insertAdjacentHTML )       {
50727             // IE
50728             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50729         } else { // Gecko
50730             var oRange = document.createRange() ;
50731             oRange.setStartBefore( element ) ;
50732             var oFragment = oRange.createContextualFragment( html );
50733             element.parentNode.insertBefore( oFragment, element ) ;
50734         }
50735     }
50736     
50737     
50738   
50739     
50740     
50741     
50742     
50743
50744 });
50745
50746 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50747
50748 function FCKeditor_OnComplete(editorInstance){
50749     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50750     f.fckEditor = editorInstance;
50751     //console.log("loaded");
50752     f.fireEvent('editorinit', f, editorInstance);
50753
50754   
50755
50756  
50757
50758
50759
50760
50761
50762
50763
50764
50765
50766
50767
50768
50769
50770
50771
50772 //<script type="text/javascript">
50773 /**
50774  * @class Roo.form.GridField
50775  * @extends Roo.form.Field
50776  * Embed a grid (or editable grid into a form)
50777  * STATUS ALPHA
50778  * 
50779  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50780  * it needs 
50781  * xgrid.store = Roo.data.Store
50782  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50783  * xgrid.store.reader = Roo.data.JsonReader 
50784  * 
50785  * 
50786  * @constructor
50787  * Creates a new GridField
50788  * @param {Object} config Configuration options
50789  */
50790 Roo.form.GridField = function(config){
50791     Roo.form.GridField.superclass.constructor.call(this, config);
50792      
50793 };
50794
50795 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50796     /**
50797      * @cfg {Number} width  - used to restrict width of grid..
50798      */
50799     width : 100,
50800     /**
50801      * @cfg {Number} height - used to restrict height of grid..
50802      */
50803     height : 50,
50804      /**
50805      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50806          * 
50807          *}
50808      */
50809     xgrid : false, 
50810     /**
50811      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50812      * {tag: "input", type: "checkbox", autocomplete: "off"})
50813      */
50814    // defaultAutoCreate : { tag: 'div' },
50815     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50816     /**
50817      * @cfg {String} addTitle Text to include for adding a title.
50818      */
50819     addTitle : false,
50820     //
50821     onResize : function(){
50822         Roo.form.Field.superclass.onResize.apply(this, arguments);
50823     },
50824
50825     initEvents : function(){
50826         // Roo.form.Checkbox.superclass.initEvents.call(this);
50827         // has no events...
50828        
50829     },
50830
50831
50832     getResizeEl : function(){
50833         return this.wrap;
50834     },
50835
50836     getPositionEl : function(){
50837         return this.wrap;
50838     },
50839
50840     // private
50841     onRender : function(ct, position){
50842         
50843         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50844         var style = this.style;
50845         delete this.style;
50846         
50847         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50848         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50849         this.viewEl = this.wrap.createChild({ tag: 'div' });
50850         if (style) {
50851             this.viewEl.applyStyles(style);
50852         }
50853         if (this.width) {
50854             this.viewEl.setWidth(this.width);
50855         }
50856         if (this.height) {
50857             this.viewEl.setHeight(this.height);
50858         }
50859         //if(this.inputValue !== undefined){
50860         //this.setValue(this.value);
50861         
50862         
50863         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50864         
50865         
50866         this.grid.render();
50867         this.grid.getDataSource().on('remove', this.refreshValue, this);
50868         this.grid.getDataSource().on('update', this.refreshValue, this);
50869         this.grid.on('afteredit', this.refreshValue, this);
50870  
50871     },
50872      
50873     
50874     /**
50875      * Sets the value of the item. 
50876      * @param {String} either an object  or a string..
50877      */
50878     setValue : function(v){
50879         //this.value = v;
50880         v = v || []; // empty set..
50881         // this does not seem smart - it really only affects memoryproxy grids..
50882         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50883             var ds = this.grid.getDataSource();
50884             // assumes a json reader..
50885             var data = {}
50886             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50887             ds.loadData( data);
50888         }
50889         // clear selection so it does not get stale.
50890         if (this.grid.sm) { 
50891             this.grid.sm.clearSelections();
50892         }
50893         
50894         Roo.form.GridField.superclass.setValue.call(this, v);
50895         this.refreshValue();
50896         // should load data in the grid really....
50897     },
50898     
50899     // private
50900     refreshValue: function() {
50901          var val = [];
50902         this.grid.getDataSource().each(function(r) {
50903             val.push(r.data);
50904         });
50905         this.el.dom.value = Roo.encode(val);
50906     }
50907     
50908      
50909     
50910     
50911 });/*
50912  * Based on:
50913  * Ext JS Library 1.1.1
50914  * Copyright(c) 2006-2007, Ext JS, LLC.
50915  *
50916  * Originally Released Under LGPL - original licence link has changed is not relivant.
50917  *
50918  * Fork - LGPL
50919  * <script type="text/javascript">
50920  */
50921 /**
50922  * @class Roo.form.DisplayField
50923  * @extends Roo.form.Field
50924  * A generic Field to display non-editable data.
50925  * @cfg {Boolean} closable (true|false) default false
50926  * @constructor
50927  * Creates a new Display Field item.
50928  * @param {Object} config Configuration options
50929  */
50930 Roo.form.DisplayField = function(config){
50931     Roo.form.DisplayField.superclass.constructor.call(this, config);
50932     
50933     this.addEvents({
50934         /**
50935          * @event close
50936          * Fires after the click the close btn
50937              * @param {Roo.form.DisplayField} this
50938              */
50939         close : true
50940     });
50941 };
50942
50943 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50944     inputType:      'hidden',
50945     allowBlank:     true,
50946     readOnly:         true,
50947     
50948  
50949     /**
50950      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50951      */
50952     focusClass : undefined,
50953     /**
50954      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50955      */
50956     fieldClass: 'x-form-field',
50957     
50958      /**
50959      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50960      */
50961     valueRenderer: undefined,
50962     
50963     width: 100,
50964     /**
50965      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50966      * {tag: "input", type: "checkbox", autocomplete: "off"})
50967      */
50968      
50969  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50970  
50971     closable : false,
50972     
50973     onResize : function(){
50974         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50975         
50976     },
50977
50978     initEvents : function(){
50979         // Roo.form.Checkbox.superclass.initEvents.call(this);
50980         // has no events...
50981         
50982         if(this.closable){
50983             this.closeEl.on('click', this.onClose, this);
50984         }
50985        
50986     },
50987
50988
50989     getResizeEl : function(){
50990         return this.wrap;
50991     },
50992
50993     getPositionEl : function(){
50994         return this.wrap;
50995     },
50996
50997     // private
50998     onRender : function(ct, position){
50999         
51000         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51001         //if(this.inputValue !== undefined){
51002         this.wrap = this.el.wrap();
51003         
51004         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51005         
51006         if(this.closable){
51007             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51008         }
51009         
51010         if (this.bodyStyle) {
51011             this.viewEl.applyStyles(this.bodyStyle);
51012         }
51013         //this.viewEl.setStyle('padding', '2px');
51014         
51015         this.setValue(this.value);
51016         
51017     },
51018 /*
51019     // private
51020     initValue : Roo.emptyFn,
51021
51022   */
51023
51024         // private
51025     onClick : function(){
51026         
51027     },
51028
51029     /**
51030      * Sets the checked state of the checkbox.
51031      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51032      */
51033     setValue : function(v){
51034         this.value = v;
51035         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51036         // this might be called before we have a dom element..
51037         if (!this.viewEl) {
51038             return;
51039         }
51040         this.viewEl.dom.innerHTML = html;
51041         Roo.form.DisplayField.superclass.setValue.call(this, v);
51042
51043     },
51044     
51045     onClose : function(e)
51046     {
51047         e.preventDefault();
51048         
51049         this.fireEvent('close', this);
51050     }
51051 });/*
51052  * 
51053  * Licence- LGPL
51054  * 
51055  */
51056
51057 /**
51058  * @class Roo.form.DayPicker
51059  * @extends Roo.form.Field
51060  * A Day picker show [M] [T] [W] ....
51061  * @constructor
51062  * Creates a new Day Picker
51063  * @param {Object} config Configuration options
51064  */
51065 Roo.form.DayPicker= function(config){
51066     Roo.form.DayPicker.superclass.constructor.call(this, config);
51067      
51068 };
51069
51070 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51071     /**
51072      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51073      */
51074     focusClass : undefined,
51075     /**
51076      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51077      */
51078     fieldClass: "x-form-field",
51079    
51080     /**
51081      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51082      * {tag: "input", type: "checkbox", autocomplete: "off"})
51083      */
51084     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51085     
51086    
51087     actionMode : 'viewEl', 
51088     //
51089     // private
51090  
51091     inputType : 'hidden',
51092     
51093      
51094     inputElement: false, // real input element?
51095     basedOn: false, // ????
51096     
51097     isFormField: true, // not sure where this is needed!!!!
51098
51099     onResize : function(){
51100         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51101         if(!this.boxLabel){
51102             this.el.alignTo(this.wrap, 'c-c');
51103         }
51104     },
51105
51106     initEvents : function(){
51107         Roo.form.Checkbox.superclass.initEvents.call(this);
51108         this.el.on("click", this.onClick,  this);
51109         this.el.on("change", this.onClick,  this);
51110     },
51111
51112
51113     getResizeEl : function(){
51114         return this.wrap;
51115     },
51116
51117     getPositionEl : function(){
51118         return this.wrap;
51119     },
51120
51121     
51122     // private
51123     onRender : function(ct, position){
51124         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51125        
51126         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51127         
51128         var r1 = '<table><tr>';
51129         var r2 = '<tr class="x-form-daypick-icons">';
51130         for (var i=0; i < 7; i++) {
51131             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51132             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51133         }
51134         
51135         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51136         viewEl.select('img').on('click', this.onClick, this);
51137         this.viewEl = viewEl;   
51138         
51139         
51140         // this will not work on Chrome!!!
51141         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51142         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51143         
51144         
51145           
51146
51147     },
51148
51149     // private
51150     initValue : Roo.emptyFn,
51151
51152     /**
51153      * Returns the checked state of the checkbox.
51154      * @return {Boolean} True if checked, else false
51155      */
51156     getValue : function(){
51157         return this.el.dom.value;
51158         
51159     },
51160
51161         // private
51162     onClick : function(e){ 
51163         //this.setChecked(!this.checked);
51164         Roo.get(e.target).toggleClass('x-menu-item-checked');
51165         this.refreshValue();
51166         //if(this.el.dom.checked != this.checked){
51167         //    this.setValue(this.el.dom.checked);
51168        // }
51169     },
51170     
51171     // private
51172     refreshValue : function()
51173     {
51174         var val = '';
51175         this.viewEl.select('img',true).each(function(e,i,n)  {
51176             val += e.is(".x-menu-item-checked") ? String(n) : '';
51177         });
51178         this.setValue(val, true);
51179     },
51180
51181     /**
51182      * Sets the checked state of the checkbox.
51183      * On is always based on a string comparison between inputValue and the param.
51184      * @param {Boolean/String} value - the value to set 
51185      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51186      */
51187     setValue : function(v,suppressEvent){
51188         if (!this.el.dom) {
51189             return;
51190         }
51191         var old = this.el.dom.value ;
51192         this.el.dom.value = v;
51193         if (suppressEvent) {
51194             return ;
51195         }
51196          
51197         // update display..
51198         this.viewEl.select('img',true).each(function(e,i,n)  {
51199             
51200             var on = e.is(".x-menu-item-checked");
51201             var newv = v.indexOf(String(n)) > -1;
51202             if (on != newv) {
51203                 e.toggleClass('x-menu-item-checked');
51204             }
51205             
51206         });
51207         
51208         
51209         this.fireEvent('change', this, v, old);
51210         
51211         
51212     },
51213    
51214     // handle setting of hidden value by some other method!!?!?
51215     setFromHidden: function()
51216     {
51217         if(!this.el){
51218             return;
51219         }
51220         //console.log("SET FROM HIDDEN");
51221         //alert('setFrom hidden');
51222         this.setValue(this.el.dom.value);
51223     },
51224     
51225     onDestroy : function()
51226     {
51227         if(this.viewEl){
51228             Roo.get(this.viewEl).remove();
51229         }
51230          
51231         Roo.form.DayPicker.superclass.onDestroy.call(this);
51232     }
51233
51234 });/*
51235  * RooJS Library 1.1.1
51236  * Copyright(c) 2008-2011  Alan Knowles
51237  *
51238  * License - LGPL
51239  */
51240  
51241
51242 /**
51243  * @class Roo.form.ComboCheck
51244  * @extends Roo.form.ComboBox
51245  * A combobox for multiple select items.
51246  *
51247  * FIXME - could do with a reset button..
51248  * 
51249  * @constructor
51250  * Create a new ComboCheck
51251  * @param {Object} config Configuration options
51252  */
51253 Roo.form.ComboCheck = function(config){
51254     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51255     // should verify some data...
51256     // like
51257     // hiddenName = required..
51258     // displayField = required
51259     // valudField == required
51260     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51261     var _t = this;
51262     Roo.each(req, function(e) {
51263         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51264             throw "Roo.form.ComboCheck : missing value for: " + e;
51265         }
51266     });
51267     
51268     
51269 };
51270
51271 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51272      
51273      
51274     editable : false,
51275      
51276     selectedClass: 'x-menu-item-checked', 
51277     
51278     // private
51279     onRender : function(ct, position){
51280         var _t = this;
51281         
51282         
51283         
51284         if(!this.tpl){
51285             var cls = 'x-combo-list';
51286
51287             
51288             this.tpl =  new Roo.Template({
51289                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51290                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51291                    '<span>{' + this.displayField + '}</span>' +
51292                     '</div>' 
51293                 
51294             });
51295         }
51296  
51297         
51298         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51299         this.view.singleSelect = false;
51300         this.view.multiSelect = true;
51301         this.view.toggleSelect = true;
51302         this.pageTb.add(new Roo.Toolbar.Fill(), {
51303             
51304             text: 'Done',
51305             handler: function()
51306             {
51307                 _t.collapse();
51308             }
51309         });
51310     },
51311     
51312     onViewOver : function(e, t){
51313         // do nothing...
51314         return;
51315         
51316     },
51317     
51318     onViewClick : function(doFocus,index){
51319         return;
51320         
51321     },
51322     select: function () {
51323         //Roo.log("SELECT CALLED");
51324     },
51325      
51326     selectByValue : function(xv, scrollIntoView){
51327         var ar = this.getValueArray();
51328         var sels = [];
51329         
51330         Roo.each(ar, function(v) {
51331             if(v === undefined || v === null){
51332                 return;
51333             }
51334             var r = this.findRecord(this.valueField, v);
51335             if(r){
51336                 sels.push(this.store.indexOf(r))
51337                 
51338             }
51339         },this);
51340         this.view.select(sels);
51341         return false;
51342     },
51343     
51344     
51345     
51346     onSelect : function(record, index){
51347        // Roo.log("onselect Called");
51348        // this is only called by the clear button now..
51349         this.view.clearSelections();
51350         this.setValue('[]');
51351         if (this.value != this.valueBefore) {
51352             this.fireEvent('change', this, this.value, this.valueBefore);
51353             this.valueBefore = this.value;
51354         }
51355     },
51356     getValueArray : function()
51357     {
51358         var ar = [] ;
51359         
51360         try {
51361             //Roo.log(this.value);
51362             if (typeof(this.value) == 'undefined') {
51363                 return [];
51364             }
51365             var ar = Roo.decode(this.value);
51366             return  ar instanceof Array ? ar : []; //?? valid?
51367             
51368         } catch(e) {
51369             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51370             return [];
51371         }
51372          
51373     },
51374     expand : function ()
51375     {
51376         
51377         Roo.form.ComboCheck.superclass.expand.call(this);
51378         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51379         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51380         
51381
51382     },
51383     
51384     collapse : function(){
51385         Roo.form.ComboCheck.superclass.collapse.call(this);
51386         var sl = this.view.getSelectedIndexes();
51387         var st = this.store;
51388         var nv = [];
51389         var tv = [];
51390         var r;
51391         Roo.each(sl, function(i) {
51392             r = st.getAt(i);
51393             nv.push(r.get(this.valueField));
51394         },this);
51395         this.setValue(Roo.encode(nv));
51396         if (this.value != this.valueBefore) {
51397
51398             this.fireEvent('change', this, this.value, this.valueBefore);
51399             this.valueBefore = this.value;
51400         }
51401         
51402     },
51403     
51404     setValue : function(v){
51405         // Roo.log(v);
51406         this.value = v;
51407         
51408         var vals = this.getValueArray();
51409         var tv = [];
51410         Roo.each(vals, function(k) {
51411             var r = this.findRecord(this.valueField, k);
51412             if(r){
51413                 tv.push(r.data[this.displayField]);
51414             }else if(this.valueNotFoundText !== undefined){
51415                 tv.push( this.valueNotFoundText );
51416             }
51417         },this);
51418        // Roo.log(tv);
51419         
51420         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51421         this.hiddenField.value = v;
51422         this.value = v;
51423     }
51424     
51425 });/*
51426  * Based on:
51427  * Ext JS Library 1.1.1
51428  * Copyright(c) 2006-2007, Ext JS, LLC.
51429  *
51430  * Originally Released Under LGPL - original licence link has changed is not relivant.
51431  *
51432  * Fork - LGPL
51433  * <script type="text/javascript">
51434  */
51435  
51436 /**
51437  * @class Roo.form.Signature
51438  * @extends Roo.form.Field
51439  * Signature field.  
51440  * @constructor
51441  * 
51442  * @param {Object} config Configuration options
51443  */
51444
51445 Roo.form.Signature = function(config){
51446     Roo.form.Signature.superclass.constructor.call(this, config);
51447     
51448     this.addEvents({// not in used??
51449          /**
51450          * @event confirm
51451          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
51452              * @param {Roo.form.Signature} combo This combo box
51453              */
51454         'confirm' : true,
51455         /**
51456          * @event reset
51457          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
51458              * @param {Roo.form.ComboBox} combo This combo box
51459              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
51460              */
51461         'reset' : true
51462     });
51463 };
51464
51465 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
51466     /**
51467      * @cfg {Object} labels Label to use when rendering a form.
51468      * defaults to 
51469      * labels : { 
51470      *      clear : "Clear",
51471      *      confirm : "Confirm"
51472      *  }
51473      */
51474     labels : { 
51475         clear : "Clear",
51476         confirm : "Confirm"
51477     },
51478     /**
51479      * @cfg {Number} width The signature panel width (defaults to 300)
51480      */
51481     width: 300,
51482     /**
51483      * @cfg {Number} height The signature panel height (defaults to 100)
51484      */
51485     height : 100,
51486     /**
51487      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
51488      */
51489     allowBlank : false,
51490     
51491     //private
51492     // {Object} signPanel The signature SVG panel element (defaults to {})
51493     signPanel : {},
51494     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
51495     isMouseDown : false,
51496     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
51497     isConfirmed : false,
51498     // {String} signatureTmp SVG mapping string (defaults to empty string)
51499     signatureTmp : '',
51500     
51501     
51502     defaultAutoCreate : { // modified by initCompnoent..
51503         tag: "input",
51504         type:"hidden"
51505     },
51506
51507     // private
51508     onRender : function(ct, position){
51509         
51510         Roo.form.Signature.superclass.onRender.call(this, ct, position);
51511         
51512         this.wrap = this.el.wrap({
51513             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
51514         });
51515         
51516         this.createToolbar(this);
51517         this.signPanel = this.wrap.createChild({
51518                 tag: 'div',
51519                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
51520             }, this.el
51521         );
51522             
51523         this.svgID = Roo.id();
51524         this.svgEl = this.signPanel.createChild({
51525               xmlns : 'http://www.w3.org/2000/svg',
51526               tag : 'svg',
51527               id : this.svgID + "-svg",
51528               width: this.width,
51529               height: this.height,
51530               viewBox: '0 0 '+this.width+' '+this.height,
51531               cn : [
51532                 {
51533                     tag: "rect",
51534                     id: this.svgID + "-svg-r",
51535                     width: this.width,
51536                     height: this.height,
51537                     fill: "#ffa"
51538                 },
51539                 {
51540                     tag: "line",
51541                     id: this.svgID + "-svg-l",
51542                     x1: "0", // start
51543                     y1: (this.height*0.8), // start set the line in 80% of height
51544                     x2: this.width, // end
51545                     y2: (this.height*0.8), // end set the line in 80% of height
51546                     'stroke': "#666",
51547                     'stroke-width': "1",
51548                     'stroke-dasharray': "3",
51549                     'shape-rendering': "crispEdges",
51550                     'pointer-events': "none"
51551                 },
51552                 {
51553                     tag: "path",
51554                     id: this.svgID + "-svg-p",
51555                     'stroke': "navy",
51556                     'stroke-width': "3",
51557                     'fill': "none",
51558                     'pointer-events': 'none'
51559                 }
51560               ]
51561         });
51562         this.createSVG();
51563         this.svgBox = this.svgEl.dom.getScreenCTM();
51564     },
51565     createSVG : function(){ 
51566         var svg = this.signPanel;
51567         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
51568         var t = this;
51569
51570         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
51571         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
51572         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
51573         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
51574         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
51575         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
51576         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
51577         
51578     },
51579     isTouchEvent : function(e){
51580         return e.type.match(/^touch/);
51581     },
51582     getCoords : function (e) {
51583         var pt    = this.svgEl.dom.createSVGPoint();
51584         pt.x = e.clientX; 
51585         pt.y = e.clientY;
51586         if (this.isTouchEvent(e)) {
51587             pt.x =  e.targetTouches[0].clientX;
51588             pt.y = e.targetTouches[0].clientY;
51589         }
51590         var a = this.svgEl.dom.getScreenCTM();
51591         var b = a.inverse();
51592         var mx = pt.matrixTransform(b);
51593         return mx.x + ',' + mx.y;
51594     },
51595     //mouse event headler 
51596     down : function (e) {
51597         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
51598         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
51599         
51600         this.isMouseDown = true;
51601         
51602         e.preventDefault();
51603     },
51604     move : function (e) {
51605         if (this.isMouseDown) {
51606             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
51607             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
51608         }
51609         
51610         e.preventDefault();
51611     },
51612     up : function (e) {
51613         this.isMouseDown = false;
51614         var sp = this.signatureTmp.split(' ');
51615         
51616         if(sp.length > 1){
51617             if(!sp[sp.length-2].match(/^L/)){
51618                 sp.pop();
51619                 sp.pop();
51620                 sp.push("");
51621                 this.signatureTmp = sp.join(" ");
51622             }
51623         }
51624         if(this.getValue() != this.signatureTmp){
51625             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51626             this.isConfirmed = false;
51627         }
51628         e.preventDefault();
51629     },
51630     
51631     /**
51632      * Protected method that will not generally be called directly. It
51633      * is called when the editor creates its toolbar. Override this method if you need to
51634      * add custom toolbar buttons.
51635      * @param {HtmlEditor} editor
51636      */
51637     createToolbar : function(editor){
51638          function btn(id, toggle, handler){
51639             var xid = fid + '-'+ id ;
51640             return {
51641                 id : xid,
51642                 cmd : id,
51643                 cls : 'x-btn-icon x-edit-'+id,
51644                 enableToggle:toggle !== false,
51645                 scope: editor, // was editor...
51646                 handler:handler||editor.relayBtnCmd,
51647                 clickEvent:'mousedown',
51648                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51649                 tabIndex:-1
51650             };
51651         }
51652         
51653         
51654         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51655         this.tb = tb;
51656         this.tb.add(
51657            {
51658                 cls : ' x-signature-btn x-signature-'+id,
51659                 scope: editor, // was editor...
51660                 handler: this.reset,
51661                 clickEvent:'mousedown',
51662                 text: this.labels.clear
51663             },
51664             {
51665                  xtype : 'Fill',
51666                  xns: Roo.Toolbar
51667             }, 
51668             {
51669                 cls : '  x-signature-btn x-signature-'+id,
51670                 scope: editor, // was editor...
51671                 handler: this.confirmHandler,
51672                 clickEvent:'mousedown',
51673                 text: this.labels.confirm
51674             }
51675         );
51676     
51677     },
51678     //public
51679     /**
51680      * when user is clicked confirm then show this image.....
51681      * 
51682      * @return {String} Image Data URI
51683      */
51684     getImageDataURI : function(){
51685         var svg = this.svgEl.dom.parentNode.innerHTML;
51686         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
51687         return src; 
51688     },
51689     /**
51690      * 
51691      * @return {Boolean} this.isConfirmed
51692      */
51693     getConfirmed : function(){
51694         return this.isConfirmed;
51695     },
51696     /**
51697      * 
51698      * @return {Number} this.width
51699      */
51700     getWidth : function(){
51701         return this.width;
51702     },
51703     /**
51704      * 
51705      * @return {Number} this.height
51706      */
51707     getHeight : function(){
51708         return this.height;
51709     },
51710     // private
51711     getSignature : function(){
51712         return this.signatureTmp;
51713     },
51714     // private
51715     reset : function(){
51716         this.signatureTmp = '';
51717         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51718         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
51719         this.isConfirmed = false;
51720         Roo.form.Signature.superclass.reset.call(this);
51721     },
51722     setSignature : function(s){
51723         this.signatureTmp = s;
51724         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51725         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51726         this.setValue(s);
51727         this.isConfirmed = false;
51728         Roo.form.Signature.superclass.reset.call(this);
51729     }, 
51730     test : function(){
51731 //        Roo.log(this.signPanel.dom.contentWindow.up())
51732     },
51733     //private
51734     setConfirmed : function(){
51735         
51736         
51737         
51738 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51739     },
51740     // private
51741     confirmHandler : function(){
51742         if(!this.getSignature()){
51743             return;
51744         }
51745         
51746         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51747         this.setValue(this.getSignature());
51748         this.isConfirmed = true;
51749         
51750         this.fireEvent('confirm', this);
51751     },
51752     // private
51753     // Subclasses should provide the validation implementation by overriding this
51754     validateValue : function(value){
51755         if(this.allowBlank){
51756             return true;
51757         }
51758         
51759         if(this.isConfirmed){
51760             return true;
51761         }
51762         return false;
51763     }
51764 });/*
51765  * Based on:
51766  * Ext JS Library 1.1.1
51767  * Copyright(c) 2006-2007, Ext JS, LLC.
51768  *
51769  * Originally Released Under LGPL - original licence link has changed is not relivant.
51770  *
51771  * Fork - LGPL
51772  * <script type="text/javascript">
51773  */
51774  
51775
51776 /**
51777  * @class Roo.form.ComboBox
51778  * @extends Roo.form.TriggerField
51779  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51780  * @constructor
51781  * Create a new ComboBox.
51782  * @param {Object} config Configuration options
51783  */
51784 Roo.form.Select = function(config){
51785     Roo.form.Select.superclass.constructor.call(this, config);
51786      
51787 };
51788
51789 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51790     /**
51791      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51792      */
51793     /**
51794      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51795      * rendering into an Roo.Editor, defaults to false)
51796      */
51797     /**
51798      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51799      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51800      */
51801     /**
51802      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51803      */
51804     /**
51805      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51806      * the dropdown list (defaults to undefined, with no header element)
51807      */
51808
51809      /**
51810      * @cfg {String/Roo.Template} tpl The template to use to render the output
51811      */
51812      
51813     // private
51814     defaultAutoCreate : {tag: "select"  },
51815     /**
51816      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51817      */
51818     listWidth: undefined,
51819     /**
51820      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51821      * mode = 'remote' or 'text' if mode = 'local')
51822      */
51823     displayField: undefined,
51824     /**
51825      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51826      * mode = 'remote' or 'value' if mode = 'local'). 
51827      * Note: use of a valueField requires the user make a selection
51828      * in order for a value to be mapped.
51829      */
51830     valueField: undefined,
51831     
51832     
51833     /**
51834      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51835      * field's data value (defaults to the underlying DOM element's name)
51836      */
51837     hiddenName: undefined,
51838     /**
51839      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51840      */
51841     listClass: '',
51842     /**
51843      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51844      */
51845     selectedClass: 'x-combo-selected',
51846     /**
51847      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51848      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51849      * which displays a downward arrow icon).
51850      */
51851     triggerClass : 'x-form-arrow-trigger',
51852     /**
51853      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51854      */
51855     shadow:'sides',
51856     /**
51857      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51858      * anchor positions (defaults to 'tl-bl')
51859      */
51860     listAlign: 'tl-bl?',
51861     /**
51862      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51863      */
51864     maxHeight: 300,
51865     /**
51866      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51867      * query specified by the allQuery config option (defaults to 'query')
51868      */
51869     triggerAction: 'query',
51870     /**
51871      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51872      * (defaults to 4, does not apply if editable = false)
51873      */
51874     minChars : 4,
51875     /**
51876      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51877      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51878      */
51879     typeAhead: false,
51880     /**
51881      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51882      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51883      */
51884     queryDelay: 500,
51885     /**
51886      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51887      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51888      */
51889     pageSize: 0,
51890     /**
51891      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51892      * when editable = true (defaults to false)
51893      */
51894     selectOnFocus:false,
51895     /**
51896      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51897      */
51898     queryParam: 'query',
51899     /**
51900      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51901      * when mode = 'remote' (defaults to 'Loading...')
51902      */
51903     loadingText: 'Loading...',
51904     /**
51905      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51906      */
51907     resizable: false,
51908     /**
51909      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51910      */
51911     handleHeight : 8,
51912     /**
51913      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51914      * traditional select (defaults to true)
51915      */
51916     editable: true,
51917     /**
51918      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51919      */
51920     allQuery: '',
51921     /**
51922      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51923      */
51924     mode: 'remote',
51925     /**
51926      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51927      * listWidth has a higher value)
51928      */
51929     minListWidth : 70,
51930     /**
51931      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51932      * allow the user to set arbitrary text into the field (defaults to false)
51933      */
51934     forceSelection:false,
51935     /**
51936      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51937      * if typeAhead = true (defaults to 250)
51938      */
51939     typeAheadDelay : 250,
51940     /**
51941      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51942      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51943      */
51944     valueNotFoundText : undefined,
51945     
51946     /**
51947      * @cfg {String} defaultValue The value displayed after loading the store.
51948      */
51949     defaultValue: '',
51950     
51951     /**
51952      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51953      */
51954     blockFocus : false,
51955     
51956     /**
51957      * @cfg {Boolean} disableClear Disable showing of clear button.
51958      */
51959     disableClear : false,
51960     /**
51961      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51962      */
51963     alwaysQuery : false,
51964     
51965     //private
51966     addicon : false,
51967     editicon: false,
51968     
51969     // element that contains real text value.. (when hidden is used..)
51970      
51971     // private
51972     onRender : function(ct, position){
51973         Roo.form.Field.prototype.onRender.call(this, ct, position);
51974         
51975         if(this.store){
51976             this.store.on('beforeload', this.onBeforeLoad, this);
51977             this.store.on('load', this.onLoad, this);
51978             this.store.on('loadexception', this.onLoadException, this);
51979             this.store.load({});
51980         }
51981         
51982         
51983         
51984     },
51985
51986     // private
51987     initEvents : function(){
51988         //Roo.form.ComboBox.superclass.initEvents.call(this);
51989  
51990     },
51991
51992     onDestroy : function(){
51993        
51994         if(this.store){
51995             this.store.un('beforeload', this.onBeforeLoad, this);
51996             this.store.un('load', this.onLoad, this);
51997             this.store.un('loadexception', this.onLoadException, this);
51998         }
51999         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52000     },
52001
52002     // private
52003     fireKey : function(e){
52004         if(e.isNavKeyPress() && !this.list.isVisible()){
52005             this.fireEvent("specialkey", this, e);
52006         }
52007     },
52008
52009     // private
52010     onResize: function(w, h){
52011         
52012         return; 
52013     
52014         
52015     },
52016
52017     /**
52018      * Allow or prevent the user from directly editing the field text.  If false is passed,
52019      * the user will only be able to select from the items defined in the dropdown list.  This method
52020      * is the runtime equivalent of setting the 'editable' config option at config time.
52021      * @param {Boolean} value True to allow the user to directly edit the field text
52022      */
52023     setEditable : function(value){
52024          
52025     },
52026
52027     // private
52028     onBeforeLoad : function(){
52029         
52030         Roo.log("Select before load");
52031         return;
52032     
52033         this.innerList.update(this.loadingText ?
52034                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52035         //this.restrictHeight();
52036         this.selectedIndex = -1;
52037     },
52038
52039     // private
52040     onLoad : function(){
52041
52042     
52043         var dom = this.el.dom;
52044         dom.innerHTML = '';
52045          var od = dom.ownerDocument;
52046          
52047         if (this.emptyText) {
52048             var op = od.createElement('option');
52049             op.setAttribute('value', '');
52050             op.innerHTML = String.format('{0}', this.emptyText);
52051             dom.appendChild(op);
52052         }
52053         if(this.store.getCount() > 0){
52054            
52055             var vf = this.valueField;
52056             var df = this.displayField;
52057             this.store.data.each(function(r) {
52058                 // which colmsn to use... testing - cdoe / title..
52059                 var op = od.createElement('option');
52060                 op.setAttribute('value', r.data[vf]);
52061                 op.innerHTML = String.format('{0}', r.data[df]);
52062                 dom.appendChild(op);
52063             });
52064             if (typeof(this.defaultValue != 'undefined')) {
52065                 this.setValue(this.defaultValue);
52066             }
52067             
52068              
52069         }else{
52070             //this.onEmptyResults();
52071         }
52072         //this.el.focus();
52073     },
52074     // private
52075     onLoadException : function()
52076     {
52077         dom.innerHTML = '';
52078             
52079         Roo.log("Select on load exception");
52080         return;
52081     
52082         this.collapse();
52083         Roo.log(this.store.reader.jsonData);
52084         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52085             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52086         }
52087         
52088         
52089     },
52090     // private
52091     onTypeAhead : function(){
52092          
52093     },
52094
52095     // private
52096     onSelect : function(record, index){
52097         Roo.log('on select?');
52098         return;
52099         if(this.fireEvent('beforeselect', this, record, index) !== false){
52100             this.setFromData(index > -1 ? record.data : false);
52101             this.collapse();
52102             this.fireEvent('select', this, record, index);
52103         }
52104     },
52105
52106     /**
52107      * Returns the currently selected field value or empty string if no value is set.
52108      * @return {String} value The selected value
52109      */
52110     getValue : function(){
52111         var dom = this.el.dom;
52112         this.value = dom.options[dom.selectedIndex].value;
52113         return this.value;
52114         
52115     },
52116
52117     /**
52118      * Clears any text/value currently set in the field
52119      */
52120     clearValue : function(){
52121         this.value = '';
52122         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52123         
52124     },
52125
52126     /**
52127      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52128      * will be displayed in the field.  If the value does not match the data value of an existing item,
52129      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52130      * Otherwise the field will be blank (although the value will still be set).
52131      * @param {String} value The value to match
52132      */
52133     setValue : function(v){
52134         var d = this.el.dom;
52135         for (var i =0; i < d.options.length;i++) {
52136             if (v == d.options[i].value) {
52137                 d.selectedIndex = i;
52138                 this.value = v;
52139                 return;
52140             }
52141         }
52142         this.clearValue();
52143     },
52144     /**
52145      * @property {Object} the last set data for the element
52146      */
52147     
52148     lastData : false,
52149     /**
52150      * Sets the value of the field based on a object which is related to the record format for the store.
52151      * @param {Object} value the value to set as. or false on reset?
52152      */
52153     setFromData : function(o){
52154         Roo.log('setfrom data?');
52155          
52156         
52157         
52158     },
52159     // private
52160     reset : function(){
52161         this.clearValue();
52162     },
52163     // private
52164     findRecord : function(prop, value){
52165         
52166         return false;
52167     
52168         var record;
52169         if(this.store.getCount() > 0){
52170             this.store.each(function(r){
52171                 if(r.data[prop] == value){
52172                     record = r;
52173                     return false;
52174                 }
52175                 return true;
52176             });
52177         }
52178         return record;
52179     },
52180     
52181     getName: function()
52182     {
52183         // returns hidden if it's set..
52184         if (!this.rendered) {return ''};
52185         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52186         
52187     },
52188      
52189
52190     
52191
52192     // private
52193     onEmptyResults : function(){
52194         Roo.log('empty results');
52195         //this.collapse();
52196     },
52197
52198     /**
52199      * Returns true if the dropdown list is expanded, else false.
52200      */
52201     isExpanded : function(){
52202         return false;
52203     },
52204
52205     /**
52206      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52207      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52208      * @param {String} value The data value of the item to select
52209      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52210      * selected item if it is not currently in view (defaults to true)
52211      * @return {Boolean} True if the value matched an item in the list, else false
52212      */
52213     selectByValue : function(v, scrollIntoView){
52214         Roo.log('select By Value');
52215         return false;
52216     
52217         if(v !== undefined && v !== null){
52218             var r = this.findRecord(this.valueField || this.displayField, v);
52219             if(r){
52220                 this.select(this.store.indexOf(r), scrollIntoView);
52221                 return true;
52222             }
52223         }
52224         return false;
52225     },
52226
52227     /**
52228      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52229      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52230      * @param {Number} index The zero-based index of the list item to select
52231      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52232      * selected item if it is not currently in view (defaults to true)
52233      */
52234     select : function(index, scrollIntoView){
52235         Roo.log('select ');
52236         return  ;
52237         
52238         this.selectedIndex = index;
52239         this.view.select(index);
52240         if(scrollIntoView !== false){
52241             var el = this.view.getNode(index);
52242             if(el){
52243                 this.innerList.scrollChildIntoView(el, false);
52244             }
52245         }
52246     },
52247
52248       
52249
52250     // private
52251     validateBlur : function(){
52252         
52253         return;
52254         
52255     },
52256
52257     // private
52258     initQuery : function(){
52259         this.doQuery(this.getRawValue());
52260     },
52261
52262     // private
52263     doForce : function(){
52264         if(this.el.dom.value.length > 0){
52265             this.el.dom.value =
52266                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52267              
52268         }
52269     },
52270
52271     /**
52272      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52273      * query allowing the query action to be canceled if needed.
52274      * @param {String} query The SQL query to execute
52275      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52276      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52277      * saved in the current store (defaults to false)
52278      */
52279     doQuery : function(q, forceAll){
52280         
52281         Roo.log('doQuery?');
52282         if(q === undefined || q === null){
52283             q = '';
52284         }
52285         var qe = {
52286             query: q,
52287             forceAll: forceAll,
52288             combo: this,
52289             cancel:false
52290         };
52291         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52292             return false;
52293         }
52294         q = qe.query;
52295         forceAll = qe.forceAll;
52296         if(forceAll === true || (q.length >= this.minChars)){
52297             if(this.lastQuery != q || this.alwaysQuery){
52298                 this.lastQuery = q;
52299                 if(this.mode == 'local'){
52300                     this.selectedIndex = -1;
52301                     if(forceAll){
52302                         this.store.clearFilter();
52303                     }else{
52304                         this.store.filter(this.displayField, q);
52305                     }
52306                     this.onLoad();
52307                 }else{
52308                     this.store.baseParams[this.queryParam] = q;
52309                     this.store.load({
52310                         params: this.getParams(q)
52311                     });
52312                     this.expand();
52313                 }
52314             }else{
52315                 this.selectedIndex = -1;
52316                 this.onLoad();   
52317             }
52318         }
52319     },
52320
52321     // private
52322     getParams : function(q){
52323         var p = {};
52324         //p[this.queryParam] = q;
52325         if(this.pageSize){
52326             p.start = 0;
52327             p.limit = this.pageSize;
52328         }
52329         return p;
52330     },
52331
52332     /**
52333      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52334      */
52335     collapse : function(){
52336         
52337     },
52338
52339     // private
52340     collapseIf : function(e){
52341         
52342     },
52343
52344     /**
52345      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52346      */
52347     expand : function(){
52348         
52349     } ,
52350
52351     // private
52352      
52353
52354     /** 
52355     * @cfg {Boolean} grow 
52356     * @hide 
52357     */
52358     /** 
52359     * @cfg {Number} growMin 
52360     * @hide 
52361     */
52362     /** 
52363     * @cfg {Number} growMax 
52364     * @hide 
52365     */
52366     /**
52367      * @hide
52368      * @method autoSize
52369      */
52370     
52371     setWidth : function()
52372     {
52373         
52374     },
52375     getResizeEl : function(){
52376         return this.el;
52377     }
52378 });//<script type="text/javasscript">
52379  
52380
52381 /**
52382  * @class Roo.DDView
52383  * A DnD enabled version of Roo.View.
52384  * @param {Element/String} container The Element in which to create the View.
52385  * @param {String} tpl The template string used to create the markup for each element of the View
52386  * @param {Object} config The configuration properties. These include all the config options of
52387  * {@link Roo.View} plus some specific to this class.<br>
52388  * <p>
52389  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52390  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52391  * <p>
52392  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52393 .x-view-drag-insert-above {
52394         border-top:1px dotted #3366cc;
52395 }
52396 .x-view-drag-insert-below {
52397         border-bottom:1px dotted #3366cc;
52398 }
52399 </code></pre>
52400  * 
52401  */
52402  
52403 Roo.DDView = function(container, tpl, config) {
52404     Roo.DDView.superclass.constructor.apply(this, arguments);
52405     this.getEl().setStyle("outline", "0px none");
52406     this.getEl().unselectable();
52407     if (this.dragGroup) {
52408         this.setDraggable(this.dragGroup.split(","));
52409     }
52410     if (this.dropGroup) {
52411         this.setDroppable(this.dropGroup.split(","));
52412     }
52413     if (this.deletable) {
52414         this.setDeletable();
52415     }
52416     this.isDirtyFlag = false;
52417         this.addEvents({
52418                 "drop" : true
52419         });
52420 };
52421
52422 Roo.extend(Roo.DDView, Roo.View, {
52423 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52424 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52425 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52426 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52427
52428         isFormField: true,
52429
52430         reset: Roo.emptyFn,
52431         
52432         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52433
52434         validate: function() {
52435                 return true;
52436         },
52437         
52438         destroy: function() {
52439                 this.purgeListeners();
52440                 this.getEl.removeAllListeners();
52441                 this.getEl().remove();
52442                 if (this.dragZone) {
52443                         if (this.dragZone.destroy) {
52444                                 this.dragZone.destroy();
52445                         }
52446                 }
52447                 if (this.dropZone) {
52448                         if (this.dropZone.destroy) {
52449                                 this.dropZone.destroy();
52450                         }
52451                 }
52452         },
52453
52454 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
52455         getName: function() {
52456                 return this.name;
52457         },
52458
52459 /**     Loads the View from a JSON string representing the Records to put into the Store. */
52460         setValue: function(v) {
52461                 if (!this.store) {
52462                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
52463                 }
52464                 var data = {};
52465                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
52466                 this.store.proxy = new Roo.data.MemoryProxy(data);
52467                 this.store.load();
52468         },
52469
52470 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
52471         getValue: function() {
52472                 var result = '(';
52473                 this.store.each(function(rec) {
52474                         result += rec.id + ',';
52475                 });
52476                 return result.substr(0, result.length - 1) + ')';
52477         },
52478         
52479         getIds: function() {
52480                 var i = 0, result = new Array(this.store.getCount());
52481                 this.store.each(function(rec) {
52482                         result[i++] = rec.id;
52483                 });
52484                 return result;
52485         },
52486         
52487         isDirty: function() {
52488                 return this.isDirtyFlag;
52489         },
52490
52491 /**
52492  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
52493  *      whole Element becomes the target, and this causes the drop gesture to append.
52494  */
52495     getTargetFromEvent : function(e) {
52496                 var target = e.getTarget();
52497                 while ((target !== null) && (target.parentNode != this.el.dom)) {
52498                 target = target.parentNode;
52499                 }
52500                 if (!target) {
52501                         target = this.el.dom.lastChild || this.el.dom;
52502                 }
52503                 return target;
52504     },
52505
52506 /**
52507  *      Create the drag data which consists of an object which has the property "ddel" as
52508  *      the drag proxy element. 
52509  */
52510     getDragData : function(e) {
52511         var target = this.findItemFromChild(e.getTarget());
52512                 if(target) {
52513                         this.handleSelection(e);
52514                         var selNodes = this.getSelectedNodes();
52515             var dragData = {
52516                 source: this,
52517                 copy: this.copy || (this.allowCopy && e.ctrlKey),
52518                 nodes: selNodes,
52519                 records: []
52520                         };
52521                         var selectedIndices = this.getSelectedIndexes();
52522                         for (var i = 0; i < selectedIndices.length; i++) {
52523                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
52524                         }
52525                         if (selNodes.length == 1) {
52526                                 dragData.ddel = target.cloneNode(true); // the div element
52527                         } else {
52528                                 var div = document.createElement('div'); // create the multi element drag "ghost"
52529                                 div.className = 'multi-proxy';
52530                                 for (var i = 0, len = selNodes.length; i < len; i++) {
52531                                         div.appendChild(selNodes[i].cloneNode(true));
52532                                 }
52533                                 dragData.ddel = div;
52534                         }
52535             //console.log(dragData)
52536             //console.log(dragData.ddel.innerHTML)
52537                         return dragData;
52538                 }
52539         //console.log('nodragData')
52540                 return false;
52541     },
52542     
52543 /**     Specify to which ddGroup items in this DDView may be dragged. */
52544     setDraggable: function(ddGroup) {
52545         if (ddGroup instanceof Array) {
52546                 Roo.each(ddGroup, this.setDraggable, this);
52547                 return;
52548         }
52549         if (this.dragZone) {
52550                 this.dragZone.addToGroup(ddGroup);
52551         } else {
52552                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
52553                                 containerScroll: true,
52554                                 ddGroup: ddGroup 
52555
52556                         });
52557 //                      Draggability implies selection. DragZone's mousedown selects the element.
52558                         if (!this.multiSelect) { this.singleSelect = true; }
52559
52560 //                      Wire the DragZone's handlers up to methods in *this*
52561                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
52562                 }
52563     },
52564
52565 /**     Specify from which ddGroup this DDView accepts drops. */
52566     setDroppable: function(ddGroup) {
52567         if (ddGroup instanceof Array) {
52568                 Roo.each(ddGroup, this.setDroppable, this);
52569                 return;
52570         }
52571         if (this.dropZone) {
52572                 this.dropZone.addToGroup(ddGroup);
52573         } else {
52574                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
52575                                 containerScroll: true,
52576                                 ddGroup: ddGroup
52577                         });
52578
52579 //                      Wire the DropZone's handlers up to methods in *this*
52580                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
52581                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
52582                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
52583                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
52584                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
52585                 }
52586     },
52587
52588 /**     Decide whether to drop above or below a View node. */
52589     getDropPoint : function(e, n, dd){
52590         if (n == this.el.dom) { return "above"; }
52591                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
52592                 var c = t + (b - t) / 2;
52593                 var y = Roo.lib.Event.getPageY(e);
52594                 if(y <= c) {
52595                         return "above";
52596                 }else{
52597                         return "below";
52598                 }
52599     },
52600
52601     onNodeEnter : function(n, dd, e, data){
52602                 return false;
52603     },
52604     
52605     onNodeOver : function(n, dd, e, data){
52606                 var pt = this.getDropPoint(e, n, dd);
52607                 // set the insert point style on the target node
52608                 var dragElClass = this.dropNotAllowed;
52609                 if (pt) {
52610                         var targetElClass;
52611                         if (pt == "above"){
52612                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
52613                                 targetElClass = "x-view-drag-insert-above";
52614                         } else {
52615                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
52616                                 targetElClass = "x-view-drag-insert-below";
52617                         }
52618                         if (this.lastInsertClass != targetElClass){
52619                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
52620                                 this.lastInsertClass = targetElClass;
52621                         }
52622                 }
52623                 return dragElClass;
52624         },
52625
52626     onNodeOut : function(n, dd, e, data){
52627                 this.removeDropIndicators(n);
52628     },
52629
52630     onNodeDrop : function(n, dd, e, data){
52631         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
52632                 return false;
52633         }
52634         var pt = this.getDropPoint(e, n, dd);
52635                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
52636                 if (pt == "below") { insertAt++; }
52637                 for (var i = 0; i < data.records.length; i++) {
52638                         var r = data.records[i];
52639                         var dup = this.store.getById(r.id);
52640                         if (dup && (dd != this.dragZone)) {
52641                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
52642                         } else {
52643                                 if (data.copy) {
52644                                         this.store.insert(insertAt++, r.copy());
52645                                 } else {
52646                                         data.source.isDirtyFlag = true;
52647                                         r.store.remove(r);
52648                                         this.store.insert(insertAt++, r);
52649                                 }
52650                                 this.isDirtyFlag = true;
52651                         }
52652                 }
52653                 this.dragZone.cachedTarget = null;
52654                 return true;
52655     },
52656
52657     removeDropIndicators : function(n){
52658                 if(n){
52659                         Roo.fly(n).removeClass([
52660                                 "x-view-drag-insert-above",
52661                                 "x-view-drag-insert-below"]);
52662                         this.lastInsertClass = "_noclass";
52663                 }
52664     },
52665
52666 /**
52667  *      Utility method. Add a delete option to the DDView's context menu.
52668  *      @param {String} imageUrl The URL of the "delete" icon image.
52669  */
52670         setDeletable: function(imageUrl) {
52671                 if (!this.singleSelect && !this.multiSelect) {
52672                         this.singleSelect = true;
52673                 }
52674                 var c = this.getContextMenu();
52675                 this.contextMenu.on("itemclick", function(item) {
52676                         switch (item.id) {
52677                                 case "delete":
52678                                         this.remove(this.getSelectedIndexes());
52679                                         break;
52680                         }
52681                 }, this);
52682                 this.contextMenu.add({
52683                         icon: imageUrl,
52684                         id: "delete",
52685                         text: 'Delete'
52686                 });
52687         },
52688         
52689 /**     Return the context menu for this DDView. */
52690         getContextMenu: function() {
52691                 if (!this.contextMenu) {
52692 //                      Create the View's context menu
52693                         this.contextMenu = new Roo.menu.Menu({
52694                                 id: this.id + "-contextmenu"
52695                         });
52696                         this.el.on("contextmenu", this.showContextMenu, this);
52697                 }
52698                 return this.contextMenu;
52699         },
52700         
52701         disableContextMenu: function() {
52702                 if (this.contextMenu) {
52703                         this.el.un("contextmenu", this.showContextMenu, this);
52704                 }
52705         },
52706
52707         showContextMenu: function(e, item) {
52708         item = this.findItemFromChild(e.getTarget());
52709                 if (item) {
52710                         e.stopEvent();
52711                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
52712                         this.contextMenu.showAt(e.getXY());
52713             }
52714     },
52715
52716 /**
52717  *      Remove {@link Roo.data.Record}s at the specified indices.
52718  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
52719  */
52720     remove: function(selectedIndices) {
52721                 selectedIndices = [].concat(selectedIndices);
52722                 for (var i = 0; i < selectedIndices.length; i++) {
52723                         var rec = this.store.getAt(selectedIndices[i]);
52724                         this.store.remove(rec);
52725                 }
52726     },
52727
52728 /**
52729  *      Double click fires the event, but also, if this is draggable, and there is only one other
52730  *      related DropZone, it transfers the selected node.
52731  */
52732     onDblClick : function(e){
52733         var item = this.findItemFromChild(e.getTarget());
52734         if(item){
52735             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52736                 return false;
52737             }
52738             if (this.dragGroup) {
52739                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52740                     while (targets.indexOf(this.dropZone) > -1) {
52741                             targets.remove(this.dropZone);
52742                                 }
52743                     if (targets.length == 1) {
52744                                         this.dragZone.cachedTarget = null;
52745                         var el = Roo.get(targets[0].getEl());
52746                         var box = el.getBox(true);
52747                         targets[0].onNodeDrop(el.dom, {
52748                                 target: el.dom,
52749                                 xy: [box.x, box.y + box.height - 1]
52750                         }, null, this.getDragData(e));
52751                     }
52752                 }
52753         }
52754     },
52755     
52756     handleSelection: function(e) {
52757                 this.dragZone.cachedTarget = null;
52758         var item = this.findItemFromChild(e.getTarget());
52759         if (!item) {
52760                 this.clearSelections(true);
52761                 return;
52762         }
52763                 if (item && (this.multiSelect || this.singleSelect)){
52764                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52765                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52766                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52767                                 this.unselect(item);
52768                         } else {
52769                                 this.select(item, this.multiSelect && e.ctrlKey);
52770                                 this.lastSelection = item;
52771                         }
52772                 }
52773     },
52774
52775     onItemClick : function(item, index, e){
52776                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52777                         return false;
52778                 }
52779                 return true;
52780     },
52781
52782     unselect : function(nodeInfo, suppressEvent){
52783                 var node = this.getNode(nodeInfo);
52784                 if(node && this.isSelected(node)){
52785                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52786                                 Roo.fly(node).removeClass(this.selectedClass);
52787                                 this.selections.remove(node);
52788                                 if(!suppressEvent){
52789                                         this.fireEvent("selectionchange", this, this.selections);
52790                                 }
52791                         }
52792                 }
52793     }
52794 });
52795 /*
52796  * Based on:
52797  * Ext JS Library 1.1.1
52798  * Copyright(c) 2006-2007, Ext JS, LLC.
52799  *
52800  * Originally Released Under LGPL - original licence link has changed is not relivant.
52801  *
52802  * Fork - LGPL
52803  * <script type="text/javascript">
52804  */
52805  
52806 /**
52807  * @class Roo.LayoutManager
52808  * @extends Roo.util.Observable
52809  * Base class for layout managers.
52810  */
52811 Roo.LayoutManager = function(container, config){
52812     Roo.LayoutManager.superclass.constructor.call(this);
52813     this.el = Roo.get(container);
52814     // ie scrollbar fix
52815     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52816         document.body.scroll = "no";
52817     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52818         this.el.position('relative');
52819     }
52820     this.id = this.el.id;
52821     this.el.addClass("x-layout-container");
52822     /** false to disable window resize monitoring @type Boolean */
52823     this.monitorWindowResize = true;
52824     this.regions = {};
52825     this.addEvents({
52826         /**
52827          * @event layout
52828          * Fires when a layout is performed. 
52829          * @param {Roo.LayoutManager} this
52830          */
52831         "layout" : true,
52832         /**
52833          * @event regionresized
52834          * Fires when the user resizes a region. 
52835          * @param {Roo.LayoutRegion} region The resized region
52836          * @param {Number} newSize The new size (width for east/west, height for north/south)
52837          */
52838         "regionresized" : true,
52839         /**
52840          * @event regioncollapsed
52841          * Fires when a region is collapsed. 
52842          * @param {Roo.LayoutRegion} region The collapsed region
52843          */
52844         "regioncollapsed" : true,
52845         /**
52846          * @event regionexpanded
52847          * Fires when a region is expanded.  
52848          * @param {Roo.LayoutRegion} region The expanded region
52849          */
52850         "regionexpanded" : true
52851     });
52852     this.updating = false;
52853     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52854 };
52855
52856 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52857     /**
52858      * Returns true if this layout is currently being updated
52859      * @return {Boolean}
52860      */
52861     isUpdating : function(){
52862         return this.updating; 
52863     },
52864     
52865     /**
52866      * Suspend the LayoutManager from doing auto-layouts while
52867      * making multiple add or remove calls
52868      */
52869     beginUpdate : function(){
52870         this.updating = true;    
52871     },
52872     
52873     /**
52874      * Restore auto-layouts and optionally disable the manager from performing a layout
52875      * @param {Boolean} noLayout true to disable a layout update 
52876      */
52877     endUpdate : function(noLayout){
52878         this.updating = false;
52879         if(!noLayout){
52880             this.layout();
52881         }    
52882     },
52883     
52884     layout: function(){
52885         
52886     },
52887     
52888     onRegionResized : function(region, newSize){
52889         this.fireEvent("regionresized", region, newSize);
52890         this.layout();
52891     },
52892     
52893     onRegionCollapsed : function(region){
52894         this.fireEvent("regioncollapsed", region);
52895     },
52896     
52897     onRegionExpanded : function(region){
52898         this.fireEvent("regionexpanded", region);
52899     },
52900         
52901     /**
52902      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52903      * performs box-model adjustments.
52904      * @return {Object} The size as an object {width: (the width), height: (the height)}
52905      */
52906     getViewSize : function(){
52907         var size;
52908         if(this.el.dom != document.body){
52909             size = this.el.getSize();
52910         }else{
52911             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52912         }
52913         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52914         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52915         return size;
52916     },
52917     
52918     /**
52919      * Returns the Element this layout is bound to.
52920      * @return {Roo.Element}
52921      */
52922     getEl : function(){
52923         return this.el;
52924     },
52925     
52926     /**
52927      * Returns the specified region.
52928      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52929      * @return {Roo.LayoutRegion}
52930      */
52931     getRegion : function(target){
52932         return this.regions[target.toLowerCase()];
52933     },
52934     
52935     onWindowResize : function(){
52936         if(this.monitorWindowResize){
52937             this.layout();
52938         }
52939     }
52940 });/*
52941  * Based on:
52942  * Ext JS Library 1.1.1
52943  * Copyright(c) 2006-2007, Ext JS, LLC.
52944  *
52945  * Originally Released Under LGPL - original licence link has changed is not relivant.
52946  *
52947  * Fork - LGPL
52948  * <script type="text/javascript">
52949  */
52950 /**
52951  * @class Roo.BorderLayout
52952  * @extends Roo.LayoutManager
52953  * @children Roo.ContentPanel
52954  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52955  * please see: <br><br>
52956  * <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>
52957  * <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>
52958  * Example:
52959  <pre><code>
52960  var layout = new Roo.BorderLayout(document.body, {
52961     north: {
52962         initialSize: 25,
52963         titlebar: false
52964     },
52965     west: {
52966         split:true,
52967         initialSize: 200,
52968         minSize: 175,
52969         maxSize: 400,
52970         titlebar: true,
52971         collapsible: true
52972     },
52973     east: {
52974         split:true,
52975         initialSize: 202,
52976         minSize: 175,
52977         maxSize: 400,
52978         titlebar: true,
52979         collapsible: true
52980     },
52981     south: {
52982         split:true,
52983         initialSize: 100,
52984         minSize: 100,
52985         maxSize: 200,
52986         titlebar: true,
52987         collapsible: true
52988     },
52989     center: {
52990         titlebar: true,
52991         autoScroll:true,
52992         resizeTabs: true,
52993         minTabWidth: 50,
52994         preferredTabWidth: 150
52995     }
52996 });
52997
52998 // shorthand
52999 var CP = Roo.ContentPanel;
53000
53001 layout.beginUpdate();
53002 layout.add("north", new CP("north", "North"));
53003 layout.add("south", new CP("south", {title: "South", closable: true}));
53004 layout.add("west", new CP("west", {title: "West"}));
53005 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53006 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53007 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53008 layout.getRegion("center").showPanel("center1");
53009 layout.endUpdate();
53010 </code></pre>
53011
53012 <b>The container the layout is rendered into can be either the body element or any other element.
53013 If it is not the body element, the container needs to either be an absolute positioned element,
53014 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53015 the container size if it is not the body element.</b>
53016
53017 * @constructor
53018 * Create a new BorderLayout
53019 * @param {String/HTMLElement/Element} container The container this layout is bound to
53020 * @param {Object} config Configuration options
53021  */
53022 Roo.BorderLayout = function(container, config){
53023     config = config || {};
53024     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53025     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53026     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53027         var target = this.factory.validRegions[i];
53028         if(config[target]){
53029             this.addRegion(target, config[target]);
53030         }
53031     }
53032 };
53033
53034 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53035         
53036         /**
53037          * @cfg {Roo.LayoutRegion} east
53038          */
53039         /**
53040          * @cfg {Roo.LayoutRegion} west
53041          */
53042         /**
53043          * @cfg {Roo.LayoutRegion} north
53044          */
53045         /**
53046          * @cfg {Roo.LayoutRegion} south
53047          */
53048         /**
53049          * @cfg {Roo.LayoutRegion} center
53050          */
53051     /**
53052      * Creates and adds a new region if it doesn't already exist.
53053      * @param {String} target The target region key (north, south, east, west or center).
53054      * @param {Object} config The regions config object
53055      * @return {BorderLayoutRegion} The new region
53056      */
53057     addRegion : function(target, config){
53058         if(!this.regions[target]){
53059             var r = this.factory.create(target, this, config);
53060             this.bindRegion(target, r);
53061         }
53062         return this.regions[target];
53063     },
53064
53065     // private (kinda)
53066     bindRegion : function(name, r){
53067         this.regions[name] = r;
53068         r.on("visibilitychange", this.layout, this);
53069         r.on("paneladded", this.layout, this);
53070         r.on("panelremoved", this.layout, this);
53071         r.on("invalidated", this.layout, this);
53072         r.on("resized", this.onRegionResized, this);
53073         r.on("collapsed", this.onRegionCollapsed, this);
53074         r.on("expanded", this.onRegionExpanded, this);
53075     },
53076
53077     /**
53078      * Performs a layout update.
53079      */
53080     layout : function(){
53081         if(this.updating) {
53082             return;
53083         }
53084         var size = this.getViewSize();
53085         var w = size.width;
53086         var h = size.height;
53087         var centerW = w;
53088         var centerH = h;
53089         var centerY = 0;
53090         var centerX = 0;
53091         //var x = 0, y = 0;
53092
53093         var rs = this.regions;
53094         var north = rs["north"];
53095         var south = rs["south"]; 
53096         var west = rs["west"];
53097         var east = rs["east"];
53098         var center = rs["center"];
53099         //if(this.hideOnLayout){ // not supported anymore
53100             //c.el.setStyle("display", "none");
53101         //}
53102         if(north && north.isVisible()){
53103             var b = north.getBox();
53104             var m = north.getMargins();
53105             b.width = w - (m.left+m.right);
53106             b.x = m.left;
53107             b.y = m.top;
53108             centerY = b.height + b.y + m.bottom;
53109             centerH -= centerY;
53110             north.updateBox(this.safeBox(b));
53111         }
53112         if(south && south.isVisible()){
53113             var b = south.getBox();
53114             var m = south.getMargins();
53115             b.width = w - (m.left+m.right);
53116             b.x = m.left;
53117             var totalHeight = (b.height + m.top + m.bottom);
53118             b.y = h - totalHeight + m.top;
53119             centerH -= totalHeight;
53120             south.updateBox(this.safeBox(b));
53121         }
53122         if(west && west.isVisible()){
53123             var b = west.getBox();
53124             var m = west.getMargins();
53125             b.height = centerH - (m.top+m.bottom);
53126             b.x = m.left;
53127             b.y = centerY + m.top;
53128             var totalWidth = (b.width + m.left + m.right);
53129             centerX += totalWidth;
53130             centerW -= totalWidth;
53131             west.updateBox(this.safeBox(b));
53132         }
53133         if(east && east.isVisible()){
53134             var b = east.getBox();
53135             var m = east.getMargins();
53136             b.height = centerH - (m.top+m.bottom);
53137             var totalWidth = (b.width + m.left + m.right);
53138             b.x = w - totalWidth + m.left;
53139             b.y = centerY + m.top;
53140             centerW -= totalWidth;
53141             east.updateBox(this.safeBox(b));
53142         }
53143         if(center){
53144             var m = center.getMargins();
53145             var centerBox = {
53146                 x: centerX + m.left,
53147                 y: centerY + m.top,
53148                 width: centerW - (m.left+m.right),
53149                 height: centerH - (m.top+m.bottom)
53150             };
53151             //if(this.hideOnLayout){
53152                 //center.el.setStyle("display", "block");
53153             //}
53154             center.updateBox(this.safeBox(centerBox));
53155         }
53156         this.el.repaint();
53157         this.fireEvent("layout", this);
53158     },
53159
53160     // private
53161     safeBox : function(box){
53162         box.width = Math.max(0, box.width);
53163         box.height = Math.max(0, box.height);
53164         return box;
53165     },
53166
53167     /**
53168      * Adds a ContentPanel (or subclass) to this layout.
53169      * @param {String} target The target region key (north, south, east, west or center).
53170      * @param {Roo.ContentPanel} panel The panel to add
53171      * @return {Roo.ContentPanel} The added panel
53172      */
53173     add : function(target, panel){
53174          
53175         target = target.toLowerCase();
53176         return this.regions[target].add(panel);
53177     },
53178
53179     /**
53180      * Remove a ContentPanel (or subclass) to this layout.
53181      * @param {String} target The target region key (north, south, east, west or center).
53182      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53183      * @return {Roo.ContentPanel} The removed panel
53184      */
53185     remove : function(target, panel){
53186         target = target.toLowerCase();
53187         return this.regions[target].remove(panel);
53188     },
53189
53190     /**
53191      * Searches all regions for a panel with the specified id
53192      * @param {String} panelId
53193      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53194      */
53195     findPanel : function(panelId){
53196         var rs = this.regions;
53197         for(var target in rs){
53198             if(typeof rs[target] != "function"){
53199                 var p = rs[target].getPanel(panelId);
53200                 if(p){
53201                     return p;
53202                 }
53203             }
53204         }
53205         return null;
53206     },
53207
53208     /**
53209      * Searches all regions for a panel with the specified id and activates (shows) it.
53210      * @param {String/ContentPanel} panelId The panels id or the panel itself
53211      * @return {Roo.ContentPanel} The shown panel or null
53212      */
53213     showPanel : function(panelId) {
53214       var rs = this.regions;
53215       for(var target in rs){
53216          var r = rs[target];
53217          if(typeof r != "function"){
53218             if(r.hasPanel(panelId)){
53219                return r.showPanel(panelId);
53220             }
53221          }
53222       }
53223       return null;
53224    },
53225
53226    /**
53227      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53228      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53229      */
53230     restoreState : function(provider){
53231         if(!provider){
53232             provider = Roo.state.Manager;
53233         }
53234         var sm = new Roo.LayoutStateManager();
53235         sm.init(this, provider);
53236     },
53237
53238     /**
53239      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53240      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53241      * a valid ContentPanel config object.  Example:
53242      * <pre><code>
53243 // Create the main layout
53244 var layout = new Roo.BorderLayout('main-ct', {
53245     west: {
53246         split:true,
53247         minSize: 175,
53248         titlebar: true
53249     },
53250     center: {
53251         title:'Components'
53252     }
53253 }, 'main-ct');
53254
53255 // Create and add multiple ContentPanels at once via configs
53256 layout.batchAdd({
53257    west: {
53258        id: 'source-files',
53259        autoCreate:true,
53260        title:'Ext Source Files',
53261        autoScroll:true,
53262        fitToFrame:true
53263    },
53264    center : {
53265        el: cview,
53266        autoScroll:true,
53267        fitToFrame:true,
53268        toolbar: tb,
53269        resizeEl:'cbody'
53270    }
53271 });
53272 </code></pre>
53273      * @param {Object} regions An object containing ContentPanel configs by region name
53274      */
53275     batchAdd : function(regions){
53276         this.beginUpdate();
53277         for(var rname in regions){
53278             var lr = this.regions[rname];
53279             if(lr){
53280                 this.addTypedPanels(lr, regions[rname]);
53281             }
53282         }
53283         this.endUpdate();
53284     },
53285
53286     // private
53287     addTypedPanels : function(lr, ps){
53288         if(typeof ps == 'string'){
53289             lr.add(new Roo.ContentPanel(ps));
53290         }
53291         else if(ps instanceof Array){
53292             for(var i =0, len = ps.length; i < len; i++){
53293                 this.addTypedPanels(lr, ps[i]);
53294             }
53295         }
53296         else if(!ps.events){ // raw config?
53297             var el = ps.el;
53298             delete ps.el; // prevent conflict
53299             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53300         }
53301         else {  // panel object assumed!
53302             lr.add(ps);
53303         }
53304     },
53305     /**
53306      * Adds a xtype elements to the layout.
53307      * <pre><code>
53308
53309 layout.addxtype({
53310        xtype : 'ContentPanel',
53311        region: 'west',
53312        items: [ .... ]
53313    }
53314 );
53315
53316 layout.addxtype({
53317         xtype : 'NestedLayoutPanel',
53318         region: 'west',
53319         layout: {
53320            center: { },
53321            west: { }   
53322         },
53323         items : [ ... list of content panels or nested layout panels.. ]
53324    }
53325 );
53326 </code></pre>
53327      * @param {Object} cfg Xtype definition of item to add.
53328      */
53329     addxtype : function(cfg)
53330     {
53331         // basically accepts a pannel...
53332         // can accept a layout region..!?!?
53333         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53334         
53335         if (!cfg.xtype.match(/Panel$/)) {
53336             return false;
53337         }
53338         var ret = false;
53339         
53340         if (typeof(cfg.region) == 'undefined') {
53341             Roo.log("Failed to add Panel, region was not set");
53342             Roo.log(cfg);
53343             return false;
53344         }
53345         var region = cfg.region;
53346         delete cfg.region;
53347         
53348           
53349         var xitems = [];
53350         if (cfg.items) {
53351             xitems = cfg.items;
53352             delete cfg.items;
53353         }
53354         var nb = false;
53355         
53356         switch(cfg.xtype) 
53357         {
53358             case 'ContentPanel':  // ContentPanel (el, cfg)
53359             case 'ScrollPanel':  // ContentPanel (el, cfg)
53360             case 'ViewPanel': 
53361                 if(cfg.autoCreate) {
53362                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53363                 } else {
53364                     var el = this.el.createChild();
53365                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53366                 }
53367                 
53368                 this.add(region, ret);
53369                 break;
53370             
53371             
53372             case 'TreePanel': // our new panel!
53373                 cfg.el = this.el.createChild();
53374                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53375                 this.add(region, ret);
53376                 break;
53377             
53378             case 'NestedLayoutPanel': 
53379                 // create a new Layout (which is  a Border Layout...
53380                 var el = this.el.createChild();
53381                 var clayout = cfg.layout;
53382                 delete cfg.layout;
53383                 clayout.items   = clayout.items  || [];
53384                 // replace this exitems with the clayout ones..
53385                 xitems = clayout.items;
53386                  
53387                 
53388                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53389                     cfg.background = false;
53390                 }
53391                 var layout = new Roo.BorderLayout(el, clayout);
53392                 
53393                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53394                 //console.log('adding nested layout panel '  + cfg.toSource());
53395                 this.add(region, ret);
53396                 nb = {}; /// find first...
53397                 break;
53398                 
53399             case 'GridPanel': 
53400             
53401                 // needs grid and region
53402                 
53403                 //var el = this.getRegion(region).el.createChild();
53404                 var el = this.el.createChild();
53405                 // create the grid first...
53406                 
53407                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53408                 delete cfg.grid;
53409                 if (region == 'center' && this.active ) {
53410                     cfg.background = false;
53411                 }
53412                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53413                 
53414                 this.add(region, ret);
53415                 if (cfg.background) {
53416                     ret.on('activate', function(gp) {
53417                         if (!gp.grid.rendered) {
53418                             gp.grid.render();
53419                         }
53420                     });
53421                 } else {
53422                     grid.render();
53423                 }
53424                 break;
53425            
53426            
53427            
53428                 
53429                 
53430                 
53431             default:
53432                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53433                     
53434                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53435                     this.add(region, ret);
53436                 } else {
53437                 
53438                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53439                     return null;
53440                 }
53441                 
53442              // GridPanel (grid, cfg)
53443             
53444         }
53445         this.beginUpdate();
53446         // add children..
53447         var region = '';
53448         var abn = {};
53449         Roo.each(xitems, function(i)  {
53450             region = nb && i.region ? i.region : false;
53451             
53452             var add = ret.addxtype(i);
53453            
53454             if (region) {
53455                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
53456                 if (!i.background) {
53457                     abn[region] = nb[region] ;
53458                 }
53459             }
53460             
53461         });
53462         this.endUpdate();
53463
53464         // make the last non-background panel active..
53465         //if (nb) { Roo.log(abn); }
53466         if (nb) {
53467             
53468             for(var r in abn) {
53469                 region = this.getRegion(r);
53470                 if (region) {
53471                     // tried using nb[r], but it does not work..
53472                      
53473                     region.showPanel(abn[r]);
53474                    
53475                 }
53476             }
53477         }
53478         return ret;
53479         
53480     }
53481 });
53482
53483 /**
53484  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
53485  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
53486  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
53487  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
53488  * <pre><code>
53489 // shorthand
53490 var CP = Roo.ContentPanel;
53491
53492 var layout = Roo.BorderLayout.create({
53493     north: {
53494         initialSize: 25,
53495         titlebar: false,
53496         panels: [new CP("north", "North")]
53497     },
53498     west: {
53499         split:true,
53500         initialSize: 200,
53501         minSize: 175,
53502         maxSize: 400,
53503         titlebar: true,
53504         collapsible: true,
53505         panels: [new CP("west", {title: "West"})]
53506     },
53507     east: {
53508         split:true,
53509         initialSize: 202,
53510         minSize: 175,
53511         maxSize: 400,
53512         titlebar: true,
53513         collapsible: true,
53514         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
53515     },
53516     south: {
53517         split:true,
53518         initialSize: 100,
53519         minSize: 100,
53520         maxSize: 200,
53521         titlebar: true,
53522         collapsible: true,
53523         panels: [new CP("south", {title: "South", closable: true})]
53524     },
53525     center: {
53526         titlebar: true,
53527         autoScroll:true,
53528         resizeTabs: true,
53529         minTabWidth: 50,
53530         preferredTabWidth: 150,
53531         panels: [
53532             new CP("center1", {title: "Close Me", closable: true}),
53533             new CP("center2", {title: "Center Panel", closable: false})
53534         ]
53535     }
53536 }, document.body);
53537
53538 layout.getRegion("center").showPanel("center1");
53539 </code></pre>
53540  * @param config
53541  * @param targetEl
53542  */
53543 Roo.BorderLayout.create = function(config, targetEl){
53544     var layout = new Roo.BorderLayout(targetEl || document.body, config);
53545     layout.beginUpdate();
53546     var regions = Roo.BorderLayout.RegionFactory.validRegions;
53547     for(var j = 0, jlen = regions.length; j < jlen; j++){
53548         var lr = regions[j];
53549         if(layout.regions[lr] && config[lr].panels){
53550             var r = layout.regions[lr];
53551             var ps = config[lr].panels;
53552             layout.addTypedPanels(r, ps);
53553         }
53554     }
53555     layout.endUpdate();
53556     return layout;
53557 };
53558
53559 // private
53560 Roo.BorderLayout.RegionFactory = {
53561     // private
53562     validRegions : ["north","south","east","west","center"],
53563
53564     // private
53565     create : function(target, mgr, config){
53566         target = target.toLowerCase();
53567         if(config.lightweight || config.basic){
53568             return new Roo.BasicLayoutRegion(mgr, config, target);
53569         }
53570         switch(target){
53571             case "north":
53572                 return new Roo.NorthLayoutRegion(mgr, config);
53573             case "south":
53574                 return new Roo.SouthLayoutRegion(mgr, config);
53575             case "east":
53576                 return new Roo.EastLayoutRegion(mgr, config);
53577             case "west":
53578                 return new Roo.WestLayoutRegion(mgr, config);
53579             case "center":
53580                 return new Roo.CenterLayoutRegion(mgr, config);
53581         }
53582         throw 'Layout region "'+target+'" not supported.';
53583     }
53584 };/*
53585  * Based on:
53586  * Ext JS Library 1.1.1
53587  * Copyright(c) 2006-2007, Ext JS, LLC.
53588  *
53589  * Originally Released Under LGPL - original licence link has changed is not relivant.
53590  *
53591  * Fork - LGPL
53592  * <script type="text/javascript">
53593  */
53594  
53595 /**
53596  * @class Roo.BasicLayoutRegion
53597  * @extends Roo.util.Observable
53598  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
53599  * and does not have a titlebar, tabs or any other features. All it does is size and position 
53600  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
53601  */
53602 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
53603     this.mgr = mgr;
53604     this.position  = pos;
53605     this.events = {
53606         /**
53607          * @scope Roo.BasicLayoutRegion
53608          */
53609         
53610         /**
53611          * @event beforeremove
53612          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
53613          * @param {Roo.LayoutRegion} this
53614          * @param {Roo.ContentPanel} panel The panel
53615          * @param {Object} e The cancel event object
53616          */
53617         "beforeremove" : true,
53618         /**
53619          * @event invalidated
53620          * Fires when the layout for this region is changed.
53621          * @param {Roo.LayoutRegion} this
53622          */
53623         "invalidated" : true,
53624         /**
53625          * @event visibilitychange
53626          * Fires when this region is shown or hidden 
53627          * @param {Roo.LayoutRegion} this
53628          * @param {Boolean} visibility true or false
53629          */
53630         "visibilitychange" : true,
53631         /**
53632          * @event paneladded
53633          * Fires when a panel is added. 
53634          * @param {Roo.LayoutRegion} this
53635          * @param {Roo.ContentPanel} panel The panel
53636          */
53637         "paneladded" : true,
53638         /**
53639          * @event panelremoved
53640          * Fires when a panel is removed. 
53641          * @param {Roo.LayoutRegion} this
53642          * @param {Roo.ContentPanel} panel The panel
53643          */
53644         "panelremoved" : true,
53645         /**
53646          * @event beforecollapse
53647          * Fires when this region before collapse.
53648          * @param {Roo.LayoutRegion} this
53649          */
53650         "beforecollapse" : true,
53651         /**
53652          * @event collapsed
53653          * Fires when this region is collapsed.
53654          * @param {Roo.LayoutRegion} this
53655          */
53656         "collapsed" : true,
53657         /**
53658          * @event expanded
53659          * Fires when this region is expanded.
53660          * @param {Roo.LayoutRegion} this
53661          */
53662         "expanded" : true,
53663         /**
53664          * @event slideshow
53665          * Fires when this region is slid into view.
53666          * @param {Roo.LayoutRegion} this
53667          */
53668         "slideshow" : true,
53669         /**
53670          * @event slidehide
53671          * Fires when this region slides out of view. 
53672          * @param {Roo.LayoutRegion} this
53673          */
53674         "slidehide" : true,
53675         /**
53676          * @event panelactivated
53677          * Fires when a panel is activated. 
53678          * @param {Roo.LayoutRegion} this
53679          * @param {Roo.ContentPanel} panel The activated panel
53680          */
53681         "panelactivated" : true,
53682         /**
53683          * @event resized
53684          * Fires when the user resizes this region. 
53685          * @param {Roo.LayoutRegion} this
53686          * @param {Number} newSize The new size (width for east/west, height for north/south)
53687          */
53688         "resized" : true
53689     };
53690     /** A collection of panels in this region. @type Roo.util.MixedCollection */
53691     this.panels = new Roo.util.MixedCollection();
53692     this.panels.getKey = this.getPanelId.createDelegate(this);
53693     this.box = null;
53694     this.activePanel = null;
53695     // ensure listeners are added...
53696     
53697     if (config.listeners || config.events) {
53698         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
53699             listeners : config.listeners || {},
53700             events : config.events || {}
53701         });
53702     }
53703     
53704     if(skipConfig !== true){
53705         this.applyConfig(config);
53706     }
53707 };
53708
53709 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
53710     getPanelId : function(p){
53711         return p.getId();
53712     },
53713     
53714     applyConfig : function(config){
53715         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53716         this.config = config;
53717         
53718     },
53719     
53720     /**
53721      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53722      * the width, for horizontal (north, south) the height.
53723      * @param {Number} newSize The new width or height
53724      */
53725     resizeTo : function(newSize){
53726         var el = this.el ? this.el :
53727                  (this.activePanel ? this.activePanel.getEl() : null);
53728         if(el){
53729             switch(this.position){
53730                 case "east":
53731                 case "west":
53732                     el.setWidth(newSize);
53733                     this.fireEvent("resized", this, newSize);
53734                 break;
53735                 case "north":
53736                 case "south":
53737                     el.setHeight(newSize);
53738                     this.fireEvent("resized", this, newSize);
53739                 break;                
53740             }
53741         }
53742     },
53743     
53744     getBox : function(){
53745         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53746     },
53747     
53748     getMargins : function(){
53749         return this.margins;
53750     },
53751     
53752     updateBox : function(box){
53753         this.box = box;
53754         var el = this.activePanel.getEl();
53755         el.dom.style.left = box.x + "px";
53756         el.dom.style.top = box.y + "px";
53757         this.activePanel.setSize(box.width, box.height);
53758     },
53759     
53760     /**
53761      * Returns the container element for this region.
53762      * @return {Roo.Element}
53763      */
53764     getEl : function(){
53765         return this.activePanel;
53766     },
53767     
53768     /**
53769      * Returns true if this region is currently visible.
53770      * @return {Boolean}
53771      */
53772     isVisible : function(){
53773         return this.activePanel ? true : false;
53774     },
53775     
53776     setActivePanel : function(panel){
53777         panel = this.getPanel(panel);
53778         if(this.activePanel && this.activePanel != panel){
53779             this.activePanel.setActiveState(false);
53780             this.activePanel.getEl().setLeftTop(-10000,-10000);
53781         }
53782         this.activePanel = panel;
53783         panel.setActiveState(true);
53784         if(this.box){
53785             panel.setSize(this.box.width, this.box.height);
53786         }
53787         this.fireEvent("panelactivated", this, panel);
53788         this.fireEvent("invalidated");
53789     },
53790     
53791     /**
53792      * Show the specified panel.
53793      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53794      * @return {Roo.ContentPanel} The shown panel or null
53795      */
53796     showPanel : function(panel){
53797         if(panel = this.getPanel(panel)){
53798             this.setActivePanel(panel);
53799         }
53800         return panel;
53801     },
53802     
53803     /**
53804      * Get the active panel for this region.
53805      * @return {Roo.ContentPanel} The active panel or null
53806      */
53807     getActivePanel : function(){
53808         return this.activePanel;
53809     },
53810     
53811     /**
53812      * Add the passed ContentPanel(s)
53813      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53814      * @return {Roo.ContentPanel} The panel added (if only one was added)
53815      */
53816     add : function(panel){
53817         if(arguments.length > 1){
53818             for(var i = 0, len = arguments.length; i < len; i++) {
53819                 this.add(arguments[i]);
53820             }
53821             return null;
53822         }
53823         if(this.hasPanel(panel)){
53824             this.showPanel(panel);
53825             return panel;
53826         }
53827         var el = panel.getEl();
53828         if(el.dom.parentNode != this.mgr.el.dom){
53829             this.mgr.el.dom.appendChild(el.dom);
53830         }
53831         if(panel.setRegion){
53832             panel.setRegion(this);
53833         }
53834         this.panels.add(panel);
53835         el.setStyle("position", "absolute");
53836         if(!panel.background){
53837             this.setActivePanel(panel);
53838             if(this.config.initialSize && this.panels.getCount()==1){
53839                 this.resizeTo(this.config.initialSize);
53840             }
53841         }
53842         this.fireEvent("paneladded", this, panel);
53843         return panel;
53844     },
53845     
53846     /**
53847      * Returns true if the panel is in this region.
53848      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53849      * @return {Boolean}
53850      */
53851     hasPanel : function(panel){
53852         if(typeof panel == "object"){ // must be panel obj
53853             panel = panel.getId();
53854         }
53855         return this.getPanel(panel) ? true : false;
53856     },
53857     
53858     /**
53859      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53860      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53861      * @param {Boolean} preservePanel Overrides the config preservePanel option
53862      * @return {Roo.ContentPanel} The panel that was removed
53863      */
53864     remove : function(panel, preservePanel){
53865         panel = this.getPanel(panel);
53866         if(!panel){
53867             return null;
53868         }
53869         var e = {};
53870         this.fireEvent("beforeremove", this, panel, e);
53871         if(e.cancel === true){
53872             return null;
53873         }
53874         var panelId = panel.getId();
53875         this.panels.removeKey(panelId);
53876         return panel;
53877     },
53878     
53879     /**
53880      * Returns the panel specified or null if it's not in this region.
53881      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53882      * @return {Roo.ContentPanel}
53883      */
53884     getPanel : function(id){
53885         if(typeof id == "object"){ // must be panel obj
53886             return id;
53887         }
53888         return this.panels.get(id);
53889     },
53890     
53891     /**
53892      * Returns this regions position (north/south/east/west/center).
53893      * @return {String} 
53894      */
53895     getPosition: function(){
53896         return this.position;    
53897     }
53898 });/*
53899  * Based on:
53900  * Ext JS Library 1.1.1
53901  * Copyright(c) 2006-2007, Ext JS, LLC.
53902  *
53903  * Originally Released Under LGPL - original licence link has changed is not relivant.
53904  *
53905  * Fork - LGPL
53906  * <script type="text/javascript">
53907  */
53908  
53909 /**
53910  * @class Roo.LayoutRegion
53911  * @extends Roo.BasicLayoutRegion
53912  * This class represents a region in a layout manager.
53913  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53914  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53915  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53916  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53917  * @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})
53918  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53919  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53920  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53921  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53922  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53923  * @cfg {String}    title           The title for the region (overrides panel titles)
53924  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53925  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53926  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53927  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53928  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53929  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53930  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53931  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53932  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53933  * @cfg {Boolean}   showPin         True to show a pin button
53934  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53935  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53936  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53937  * @cfg {Number}    width           For East/West panels
53938  * @cfg {Number}    height          For North/South panels
53939  * @cfg {Boolean}   split           To show the splitter
53940  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53941  */
53942 Roo.LayoutRegion = function(mgr, config, pos){
53943     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53944     var dh = Roo.DomHelper;
53945     /** This region's container element 
53946     * @type Roo.Element */
53947     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53948     /** This region's title element 
53949     * @type Roo.Element */
53950
53951     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53952         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53953         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53954     ]}, true);
53955     this.titleEl.enableDisplayMode();
53956     /** This region's title text element 
53957     * @type HTMLElement */
53958     this.titleTextEl = this.titleEl.dom.firstChild;
53959     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53960     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53961     this.closeBtn.enableDisplayMode();
53962     this.closeBtn.on("click", this.closeClicked, this);
53963     this.closeBtn.hide();
53964
53965     this.createBody(config);
53966     this.visible = true;
53967     this.collapsed = false;
53968
53969     if(config.hideWhenEmpty){
53970         this.hide();
53971         this.on("paneladded", this.validateVisibility, this);
53972         this.on("panelremoved", this.validateVisibility, this);
53973     }
53974     this.applyConfig(config);
53975 };
53976
53977 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53978
53979     createBody : function(){
53980         /** This region's body element 
53981         * @type Roo.Element */
53982         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53983     },
53984
53985     applyConfig : function(c){
53986         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53987             var dh = Roo.DomHelper;
53988             if(c.titlebar !== false){
53989                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53990                 this.collapseBtn.on("click", this.collapse, this);
53991                 this.collapseBtn.enableDisplayMode();
53992
53993                 if(c.showPin === true || this.showPin){
53994                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53995                     this.stickBtn.enableDisplayMode();
53996                     this.stickBtn.on("click", this.expand, this);
53997                     this.stickBtn.hide();
53998                 }
53999             }
54000             /** This region's collapsed element
54001             * @type Roo.Element */
54002             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54003                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54004             ]}, true);
54005             if(c.floatable !== false){
54006                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54007                this.collapsedEl.on("click", this.collapseClick, this);
54008             }
54009
54010             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54011                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54012                    id: "message", unselectable: "on", style:{"float":"left"}});
54013                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54014              }
54015             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54016             this.expandBtn.on("click", this.expand, this);
54017         }
54018         if(this.collapseBtn){
54019             this.collapseBtn.setVisible(c.collapsible == true);
54020         }
54021         this.cmargins = c.cmargins || this.cmargins ||
54022                          (this.position == "west" || this.position == "east" ?
54023                              {top: 0, left: 2, right:2, bottom: 0} :
54024                              {top: 2, left: 0, right:0, bottom: 2});
54025         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54026         this.bottomTabs = c.tabPosition != "top";
54027         this.autoScroll = c.autoScroll || false;
54028         if(this.autoScroll){
54029             this.bodyEl.setStyle("overflow", "auto");
54030         }else{
54031             this.bodyEl.setStyle("overflow", "hidden");
54032         }
54033         //if(c.titlebar !== false){
54034             if((!c.titlebar && !c.title) || c.titlebar === false){
54035                 this.titleEl.hide();
54036             }else{
54037                 this.titleEl.show();
54038                 if(c.title){
54039                     this.titleTextEl.innerHTML = c.title;
54040                 }
54041             }
54042         //}
54043         this.duration = c.duration || .30;
54044         this.slideDuration = c.slideDuration || .45;
54045         this.config = c;
54046         if(c.collapsed){
54047             this.collapse(true);
54048         }
54049         if(c.hidden){
54050             this.hide();
54051         }
54052     },
54053     /**
54054      * Returns true if this region is currently visible.
54055      * @return {Boolean}
54056      */
54057     isVisible : function(){
54058         return this.visible;
54059     },
54060
54061     /**
54062      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54063      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54064      */
54065     setCollapsedTitle : function(title){
54066         title = title || "&#160;";
54067         if(this.collapsedTitleTextEl){
54068             this.collapsedTitleTextEl.innerHTML = title;
54069         }
54070     },
54071
54072     getBox : function(){
54073         var b;
54074         if(!this.collapsed){
54075             b = this.el.getBox(false, true);
54076         }else{
54077             b = this.collapsedEl.getBox(false, true);
54078         }
54079         return b;
54080     },
54081
54082     getMargins : function(){
54083         return this.collapsed ? this.cmargins : this.margins;
54084     },
54085
54086     highlight : function(){
54087         this.el.addClass("x-layout-panel-dragover");
54088     },
54089
54090     unhighlight : function(){
54091         this.el.removeClass("x-layout-panel-dragover");
54092     },
54093
54094     updateBox : function(box){
54095         this.box = box;
54096         if(!this.collapsed){
54097             this.el.dom.style.left = box.x + "px";
54098             this.el.dom.style.top = box.y + "px";
54099             this.updateBody(box.width, box.height);
54100         }else{
54101             this.collapsedEl.dom.style.left = box.x + "px";
54102             this.collapsedEl.dom.style.top = box.y + "px";
54103             this.collapsedEl.setSize(box.width, box.height);
54104         }
54105         if(this.tabs){
54106             this.tabs.autoSizeTabs();
54107         }
54108     },
54109
54110     updateBody : function(w, h){
54111         if(w !== null){
54112             this.el.setWidth(w);
54113             w -= this.el.getBorderWidth("rl");
54114             if(this.config.adjustments){
54115                 w += this.config.adjustments[0];
54116             }
54117         }
54118         if(h !== null){
54119             this.el.setHeight(h);
54120             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54121             h -= this.el.getBorderWidth("tb");
54122             if(this.config.adjustments){
54123                 h += this.config.adjustments[1];
54124             }
54125             this.bodyEl.setHeight(h);
54126             if(this.tabs){
54127                 h = this.tabs.syncHeight(h);
54128             }
54129         }
54130         if(this.panelSize){
54131             w = w !== null ? w : this.panelSize.width;
54132             h = h !== null ? h : this.panelSize.height;
54133         }
54134         if(this.activePanel){
54135             var el = this.activePanel.getEl();
54136             w = w !== null ? w : el.getWidth();
54137             h = h !== null ? h : el.getHeight();
54138             this.panelSize = {width: w, height: h};
54139             this.activePanel.setSize(w, h);
54140         }
54141         if(Roo.isIE && this.tabs){
54142             this.tabs.el.repaint();
54143         }
54144     },
54145
54146     /**
54147      * Returns the container element for this region.
54148      * @return {Roo.Element}
54149      */
54150     getEl : function(){
54151         return this.el;
54152     },
54153
54154     /**
54155      * Hides this region.
54156      */
54157     hide : function(){
54158         if(!this.collapsed){
54159             this.el.dom.style.left = "-2000px";
54160             this.el.hide();
54161         }else{
54162             this.collapsedEl.dom.style.left = "-2000px";
54163             this.collapsedEl.hide();
54164         }
54165         this.visible = false;
54166         this.fireEvent("visibilitychange", this, false);
54167     },
54168
54169     /**
54170      * Shows this region if it was previously hidden.
54171      */
54172     show : function(){
54173         if(!this.collapsed){
54174             this.el.show();
54175         }else{
54176             this.collapsedEl.show();
54177         }
54178         this.visible = true;
54179         this.fireEvent("visibilitychange", this, true);
54180     },
54181
54182     closeClicked : function(){
54183         if(this.activePanel){
54184             this.remove(this.activePanel);
54185         }
54186     },
54187
54188     collapseClick : function(e){
54189         if(this.isSlid){
54190            e.stopPropagation();
54191            this.slideIn();
54192         }else{
54193            e.stopPropagation();
54194            this.slideOut();
54195         }
54196     },
54197
54198     /**
54199      * Collapses this region.
54200      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54201      */
54202     collapse : function(skipAnim, skipCheck){
54203         if(this.collapsed) {
54204             return;
54205         }
54206         
54207         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54208             
54209             this.collapsed = true;
54210             if(this.split){
54211                 this.split.el.hide();
54212             }
54213             if(this.config.animate && skipAnim !== true){
54214                 this.fireEvent("invalidated", this);
54215                 this.animateCollapse();
54216             }else{
54217                 this.el.setLocation(-20000,-20000);
54218                 this.el.hide();
54219                 this.collapsedEl.show();
54220                 this.fireEvent("collapsed", this);
54221                 this.fireEvent("invalidated", this);
54222             }
54223         }
54224         
54225     },
54226
54227     animateCollapse : function(){
54228         // overridden
54229     },
54230
54231     /**
54232      * Expands this region if it was previously collapsed.
54233      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54234      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54235      */
54236     expand : function(e, skipAnim){
54237         if(e) {
54238             e.stopPropagation();
54239         }
54240         if(!this.collapsed || this.el.hasActiveFx()) {
54241             return;
54242         }
54243         if(this.isSlid){
54244             this.afterSlideIn();
54245             skipAnim = true;
54246         }
54247         this.collapsed = false;
54248         if(this.config.animate && skipAnim !== true){
54249             this.animateExpand();
54250         }else{
54251             this.el.show();
54252             if(this.split){
54253                 this.split.el.show();
54254             }
54255             this.collapsedEl.setLocation(-2000,-2000);
54256             this.collapsedEl.hide();
54257             this.fireEvent("invalidated", this);
54258             this.fireEvent("expanded", this);
54259         }
54260     },
54261
54262     animateExpand : function(){
54263         // overridden
54264     },
54265
54266     initTabs : function()
54267     {
54268         this.bodyEl.setStyle("overflow", "hidden");
54269         var ts = new Roo.TabPanel(
54270                 this.bodyEl.dom,
54271                 {
54272                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54273                     disableTooltips: this.config.disableTabTips,
54274                     toolbar : this.config.toolbar
54275                 }
54276         );
54277         if(this.config.hideTabs){
54278             ts.stripWrap.setDisplayed(false);
54279         }
54280         this.tabs = ts;
54281         ts.resizeTabs = this.config.resizeTabs === true;
54282         ts.minTabWidth = this.config.minTabWidth || 40;
54283         ts.maxTabWidth = this.config.maxTabWidth || 250;
54284         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54285         ts.monitorResize = false;
54286         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54287         ts.bodyEl.addClass('x-layout-tabs-body');
54288         this.panels.each(this.initPanelAsTab, this);
54289     },
54290
54291     initPanelAsTab : function(panel){
54292         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54293                     this.config.closeOnTab && panel.isClosable());
54294         if(panel.tabTip !== undefined){
54295             ti.setTooltip(panel.tabTip);
54296         }
54297         ti.on("activate", function(){
54298               this.setActivePanel(panel);
54299         }, this);
54300         if(this.config.closeOnTab){
54301             ti.on("beforeclose", function(t, e){
54302                 e.cancel = true;
54303                 this.remove(panel);
54304             }, this);
54305         }
54306         return ti;
54307     },
54308
54309     updatePanelTitle : function(panel, title){
54310         if(this.activePanel == panel){
54311             this.updateTitle(title);
54312         }
54313         if(this.tabs){
54314             var ti = this.tabs.getTab(panel.getEl().id);
54315             ti.setText(title);
54316             if(panel.tabTip !== undefined){
54317                 ti.setTooltip(panel.tabTip);
54318             }
54319         }
54320     },
54321
54322     updateTitle : function(title){
54323         if(this.titleTextEl && !this.config.title){
54324             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54325         }
54326     },
54327
54328     setActivePanel : function(panel){
54329         panel = this.getPanel(panel);
54330         if(this.activePanel && this.activePanel != panel){
54331             this.activePanel.setActiveState(false);
54332         }
54333         this.activePanel = panel;
54334         panel.setActiveState(true);
54335         if(this.panelSize){
54336             panel.setSize(this.panelSize.width, this.panelSize.height);
54337         }
54338         if(this.closeBtn){
54339             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54340         }
54341         this.updateTitle(panel.getTitle());
54342         if(this.tabs){
54343             this.fireEvent("invalidated", this);
54344         }
54345         this.fireEvent("panelactivated", this, panel);
54346     },
54347
54348     /**
54349      * Shows the specified panel.
54350      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54351      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54352      */
54353     showPanel : function(panel)
54354     {
54355         panel = this.getPanel(panel);
54356         if(panel){
54357             if(this.tabs){
54358                 var tab = this.tabs.getTab(panel.getEl().id);
54359                 if(tab.isHidden()){
54360                     this.tabs.unhideTab(tab.id);
54361                 }
54362                 tab.activate();
54363             }else{
54364                 this.setActivePanel(panel);
54365             }
54366         }
54367         return panel;
54368     },
54369
54370     /**
54371      * Get the active panel for this region.
54372      * @return {Roo.ContentPanel} The active panel or null
54373      */
54374     getActivePanel : function(){
54375         return this.activePanel;
54376     },
54377
54378     validateVisibility : function(){
54379         if(this.panels.getCount() < 1){
54380             this.updateTitle("&#160;");
54381             this.closeBtn.hide();
54382             this.hide();
54383         }else{
54384             if(!this.isVisible()){
54385                 this.show();
54386             }
54387         }
54388     },
54389
54390     /**
54391      * Adds the passed ContentPanel(s) to this region.
54392      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54393      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54394      */
54395     add : function(panel){
54396         if(arguments.length > 1){
54397             for(var i = 0, len = arguments.length; i < len; i++) {
54398                 this.add(arguments[i]);
54399             }
54400             return null;
54401         }
54402         if(this.hasPanel(panel)){
54403             this.showPanel(panel);
54404             return panel;
54405         }
54406         panel.setRegion(this);
54407         this.panels.add(panel);
54408         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54409             this.bodyEl.dom.appendChild(panel.getEl().dom);
54410             if(panel.background !== true){
54411                 this.setActivePanel(panel);
54412             }
54413             this.fireEvent("paneladded", this, panel);
54414             return panel;
54415         }
54416         if(!this.tabs){
54417             this.initTabs();
54418         }else{
54419             this.initPanelAsTab(panel);
54420         }
54421         if(panel.background !== true){
54422             this.tabs.activate(panel.getEl().id);
54423         }
54424         this.fireEvent("paneladded", this, panel);
54425         return panel;
54426     },
54427
54428     /**
54429      * Hides the tab for the specified panel.
54430      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54431      */
54432     hidePanel : function(panel){
54433         if(this.tabs && (panel = this.getPanel(panel))){
54434             this.tabs.hideTab(panel.getEl().id);
54435         }
54436     },
54437
54438     /**
54439      * Unhides the tab for a previously hidden panel.
54440      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54441      */
54442     unhidePanel : function(panel){
54443         if(this.tabs && (panel = this.getPanel(panel))){
54444             this.tabs.unhideTab(panel.getEl().id);
54445         }
54446     },
54447
54448     clearPanels : function(){
54449         while(this.panels.getCount() > 0){
54450              this.remove(this.panels.first());
54451         }
54452     },
54453
54454     /**
54455      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54456      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54457      * @param {Boolean} preservePanel Overrides the config preservePanel option
54458      * @return {Roo.ContentPanel} The panel that was removed
54459      */
54460     remove : function(panel, preservePanel){
54461         panel = this.getPanel(panel);
54462         if(!panel){
54463             return null;
54464         }
54465         var e = {};
54466         this.fireEvent("beforeremove", this, panel, e);
54467         if(e.cancel === true){
54468             return null;
54469         }
54470         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
54471         var panelId = panel.getId();
54472         this.panels.removeKey(panelId);
54473         if(preservePanel){
54474             document.body.appendChild(panel.getEl().dom);
54475         }
54476         if(this.tabs){
54477             this.tabs.removeTab(panel.getEl().id);
54478         }else if (!preservePanel){
54479             this.bodyEl.dom.removeChild(panel.getEl().dom);
54480         }
54481         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
54482             var p = this.panels.first();
54483             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
54484             tempEl.appendChild(p.getEl().dom);
54485             this.bodyEl.update("");
54486             this.bodyEl.dom.appendChild(p.getEl().dom);
54487             tempEl = null;
54488             this.updateTitle(p.getTitle());
54489             this.tabs = null;
54490             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54491             this.setActivePanel(p);
54492         }
54493         panel.setRegion(null);
54494         if(this.activePanel == panel){
54495             this.activePanel = null;
54496         }
54497         if(this.config.autoDestroy !== false && preservePanel !== true){
54498             try{panel.destroy();}catch(e){}
54499         }
54500         this.fireEvent("panelremoved", this, panel);
54501         return panel;
54502     },
54503
54504     /**
54505      * Returns the TabPanel component used by this region
54506      * @return {Roo.TabPanel}
54507      */
54508     getTabs : function(){
54509         return this.tabs;
54510     },
54511
54512     createTool : function(parentEl, className){
54513         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
54514             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
54515         btn.addClassOnOver("x-layout-tools-button-over");
54516         return btn;
54517     }
54518 });/*
54519  * Based on:
54520  * Ext JS Library 1.1.1
54521  * Copyright(c) 2006-2007, Ext JS, LLC.
54522  *
54523  * Originally Released Under LGPL - original licence link has changed is not relivant.
54524  *
54525  * Fork - LGPL
54526  * <script type="text/javascript">
54527  */
54528  
54529
54530
54531 /**
54532  * @class Roo.SplitLayoutRegion
54533  * @extends Roo.LayoutRegion
54534  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
54535  */
54536 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
54537     this.cursor = cursor;
54538     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
54539 };
54540
54541 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
54542     splitTip : "Drag to resize.",
54543     collapsibleSplitTip : "Drag to resize. Double click to hide.",
54544     useSplitTips : false,
54545
54546     applyConfig : function(config){
54547         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
54548         if(config.split){
54549             if(!this.split){
54550                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
54551                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
54552                 /** The SplitBar for this region 
54553                 * @type Roo.SplitBar */
54554                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
54555                 this.split.on("moved", this.onSplitMove, this);
54556                 this.split.useShim = config.useShim === true;
54557                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
54558                 if(this.useSplitTips){
54559                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
54560                 }
54561                 if(config.collapsible){
54562                     this.split.el.on("dblclick", this.collapse,  this);
54563                 }
54564             }
54565             if(typeof config.minSize != "undefined"){
54566                 this.split.minSize = config.minSize;
54567             }
54568             if(typeof config.maxSize != "undefined"){
54569                 this.split.maxSize = config.maxSize;
54570             }
54571             if(config.hideWhenEmpty || config.hidden || config.collapsed){
54572                 this.hideSplitter();
54573             }
54574         }
54575     },
54576
54577     getHMaxSize : function(){
54578          var cmax = this.config.maxSize || 10000;
54579          var center = this.mgr.getRegion("center");
54580          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
54581     },
54582
54583     getVMaxSize : function(){
54584          var cmax = this.config.maxSize || 10000;
54585          var center = this.mgr.getRegion("center");
54586          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
54587     },
54588
54589     onSplitMove : function(split, newSize){
54590         this.fireEvent("resized", this, newSize);
54591     },
54592     
54593     /** 
54594      * Returns the {@link Roo.SplitBar} for this region.
54595      * @return {Roo.SplitBar}
54596      */
54597     getSplitBar : function(){
54598         return this.split;
54599     },
54600     
54601     hide : function(){
54602         this.hideSplitter();
54603         Roo.SplitLayoutRegion.superclass.hide.call(this);
54604     },
54605
54606     hideSplitter : function(){
54607         if(this.split){
54608             this.split.el.setLocation(-2000,-2000);
54609             this.split.el.hide();
54610         }
54611     },
54612
54613     show : function(){
54614         if(this.split){
54615             this.split.el.show();
54616         }
54617         Roo.SplitLayoutRegion.superclass.show.call(this);
54618     },
54619     
54620     beforeSlide: function(){
54621         if(Roo.isGecko){// firefox overflow auto bug workaround
54622             this.bodyEl.clip();
54623             if(this.tabs) {
54624                 this.tabs.bodyEl.clip();
54625             }
54626             if(this.activePanel){
54627                 this.activePanel.getEl().clip();
54628                 
54629                 if(this.activePanel.beforeSlide){
54630                     this.activePanel.beforeSlide();
54631                 }
54632             }
54633         }
54634     },
54635     
54636     afterSlide : function(){
54637         if(Roo.isGecko){// firefox overflow auto bug workaround
54638             this.bodyEl.unclip();
54639             if(this.tabs) {
54640                 this.tabs.bodyEl.unclip();
54641             }
54642             if(this.activePanel){
54643                 this.activePanel.getEl().unclip();
54644                 if(this.activePanel.afterSlide){
54645                     this.activePanel.afterSlide();
54646                 }
54647             }
54648         }
54649     },
54650
54651     initAutoHide : function(){
54652         if(this.autoHide !== false){
54653             if(!this.autoHideHd){
54654                 var st = new Roo.util.DelayedTask(this.slideIn, this);
54655                 this.autoHideHd = {
54656                     "mouseout": function(e){
54657                         if(!e.within(this.el, true)){
54658                             st.delay(500);
54659                         }
54660                     },
54661                     "mouseover" : function(e){
54662                         st.cancel();
54663                     },
54664                     scope : this
54665                 };
54666             }
54667             this.el.on(this.autoHideHd);
54668         }
54669     },
54670
54671     clearAutoHide : function(){
54672         if(this.autoHide !== false){
54673             this.el.un("mouseout", this.autoHideHd.mouseout);
54674             this.el.un("mouseover", this.autoHideHd.mouseover);
54675         }
54676     },
54677
54678     clearMonitor : function(){
54679         Roo.get(document).un("click", this.slideInIf, this);
54680     },
54681
54682     // these names are backwards but not changed for compat
54683     slideOut : function(){
54684         if(this.isSlid || this.el.hasActiveFx()){
54685             return;
54686         }
54687         this.isSlid = true;
54688         if(this.collapseBtn){
54689             this.collapseBtn.hide();
54690         }
54691         this.closeBtnState = this.closeBtn.getStyle('display');
54692         this.closeBtn.hide();
54693         if(this.stickBtn){
54694             this.stickBtn.show();
54695         }
54696         this.el.show();
54697         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
54698         this.beforeSlide();
54699         this.el.setStyle("z-index", 10001);
54700         this.el.slideIn(this.getSlideAnchor(), {
54701             callback: function(){
54702                 this.afterSlide();
54703                 this.initAutoHide();
54704                 Roo.get(document).on("click", this.slideInIf, this);
54705                 this.fireEvent("slideshow", this);
54706             },
54707             scope: this,
54708             block: true
54709         });
54710     },
54711
54712     afterSlideIn : function(){
54713         this.clearAutoHide();
54714         this.isSlid = false;
54715         this.clearMonitor();
54716         this.el.setStyle("z-index", "");
54717         if(this.collapseBtn){
54718             this.collapseBtn.show();
54719         }
54720         this.closeBtn.setStyle('display', this.closeBtnState);
54721         if(this.stickBtn){
54722             this.stickBtn.hide();
54723         }
54724         this.fireEvent("slidehide", this);
54725     },
54726
54727     slideIn : function(cb){
54728         if(!this.isSlid || this.el.hasActiveFx()){
54729             Roo.callback(cb);
54730             return;
54731         }
54732         this.isSlid = false;
54733         this.beforeSlide();
54734         this.el.slideOut(this.getSlideAnchor(), {
54735             callback: function(){
54736                 this.el.setLeftTop(-10000, -10000);
54737                 this.afterSlide();
54738                 this.afterSlideIn();
54739                 Roo.callback(cb);
54740             },
54741             scope: this,
54742             block: true
54743         });
54744     },
54745     
54746     slideInIf : function(e){
54747         if(!e.within(this.el)){
54748             this.slideIn();
54749         }
54750     },
54751
54752     animateCollapse : function(){
54753         this.beforeSlide();
54754         this.el.setStyle("z-index", 20000);
54755         var anchor = this.getSlideAnchor();
54756         this.el.slideOut(anchor, {
54757             callback : function(){
54758                 this.el.setStyle("z-index", "");
54759                 this.collapsedEl.slideIn(anchor, {duration:.3});
54760                 this.afterSlide();
54761                 this.el.setLocation(-10000,-10000);
54762                 this.el.hide();
54763                 this.fireEvent("collapsed", this);
54764             },
54765             scope: this,
54766             block: true
54767         });
54768     },
54769
54770     animateExpand : function(){
54771         this.beforeSlide();
54772         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54773         this.el.setStyle("z-index", 20000);
54774         this.collapsedEl.hide({
54775             duration:.1
54776         });
54777         this.el.slideIn(this.getSlideAnchor(), {
54778             callback : function(){
54779                 this.el.setStyle("z-index", "");
54780                 this.afterSlide();
54781                 if(this.split){
54782                     this.split.el.show();
54783                 }
54784                 this.fireEvent("invalidated", this);
54785                 this.fireEvent("expanded", this);
54786             },
54787             scope: this,
54788             block: true
54789         });
54790     },
54791
54792     anchors : {
54793         "west" : "left",
54794         "east" : "right",
54795         "north" : "top",
54796         "south" : "bottom"
54797     },
54798
54799     sanchors : {
54800         "west" : "l",
54801         "east" : "r",
54802         "north" : "t",
54803         "south" : "b"
54804     },
54805
54806     canchors : {
54807         "west" : "tl-tr",
54808         "east" : "tr-tl",
54809         "north" : "tl-bl",
54810         "south" : "bl-tl"
54811     },
54812
54813     getAnchor : function(){
54814         return this.anchors[this.position];
54815     },
54816
54817     getCollapseAnchor : function(){
54818         return this.canchors[this.position];
54819     },
54820
54821     getSlideAnchor : function(){
54822         return this.sanchors[this.position];
54823     },
54824
54825     getAlignAdj : function(){
54826         var cm = this.cmargins;
54827         switch(this.position){
54828             case "west":
54829                 return [0, 0];
54830             break;
54831             case "east":
54832                 return [0, 0];
54833             break;
54834             case "north":
54835                 return [0, 0];
54836             break;
54837             case "south":
54838                 return [0, 0];
54839             break;
54840         }
54841     },
54842
54843     getExpandAdj : function(){
54844         var c = this.collapsedEl, cm = this.cmargins;
54845         switch(this.position){
54846             case "west":
54847                 return [-(cm.right+c.getWidth()+cm.left), 0];
54848             break;
54849             case "east":
54850                 return [cm.right+c.getWidth()+cm.left, 0];
54851             break;
54852             case "north":
54853                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54854             break;
54855             case "south":
54856                 return [0, cm.top+cm.bottom+c.getHeight()];
54857             break;
54858         }
54859     }
54860 });/*
54861  * Based on:
54862  * Ext JS Library 1.1.1
54863  * Copyright(c) 2006-2007, Ext JS, LLC.
54864  *
54865  * Originally Released Under LGPL - original licence link has changed is not relivant.
54866  *
54867  * Fork - LGPL
54868  * <script type="text/javascript">
54869  */
54870 /*
54871  * These classes are private internal classes
54872  */
54873 Roo.CenterLayoutRegion = function(mgr, config){
54874     Roo.LayoutRegion.call(this, mgr, config, "center");
54875     this.visible = true;
54876     this.minWidth = config.minWidth || 20;
54877     this.minHeight = config.minHeight || 20;
54878 };
54879
54880 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54881     hide : function(){
54882         // center panel can't be hidden
54883     },
54884     
54885     show : function(){
54886         // center panel can't be hidden
54887     },
54888     
54889     getMinWidth: function(){
54890         return this.minWidth;
54891     },
54892     
54893     getMinHeight: function(){
54894         return this.minHeight;
54895     }
54896 });
54897
54898
54899 Roo.NorthLayoutRegion = function(mgr, config){
54900     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54901     if(this.split){
54902         this.split.placement = Roo.SplitBar.TOP;
54903         this.split.orientation = Roo.SplitBar.VERTICAL;
54904         this.split.el.addClass("x-layout-split-v");
54905     }
54906     var size = config.initialSize || config.height;
54907     if(typeof size != "undefined"){
54908         this.el.setHeight(size);
54909     }
54910 };
54911 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54912     orientation: Roo.SplitBar.VERTICAL,
54913     getBox : function(){
54914         if(this.collapsed){
54915             return this.collapsedEl.getBox();
54916         }
54917         var box = this.el.getBox();
54918         if(this.split){
54919             box.height += this.split.el.getHeight();
54920         }
54921         return box;
54922     },
54923     
54924     updateBox : function(box){
54925         if(this.split && !this.collapsed){
54926             box.height -= this.split.el.getHeight();
54927             this.split.el.setLeft(box.x);
54928             this.split.el.setTop(box.y+box.height);
54929             this.split.el.setWidth(box.width);
54930         }
54931         if(this.collapsed){
54932             this.updateBody(box.width, null);
54933         }
54934         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54935     }
54936 });
54937
54938 Roo.SouthLayoutRegion = function(mgr, config){
54939     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54940     if(this.split){
54941         this.split.placement = Roo.SplitBar.BOTTOM;
54942         this.split.orientation = Roo.SplitBar.VERTICAL;
54943         this.split.el.addClass("x-layout-split-v");
54944     }
54945     var size = config.initialSize || config.height;
54946     if(typeof size != "undefined"){
54947         this.el.setHeight(size);
54948     }
54949 };
54950 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54951     orientation: Roo.SplitBar.VERTICAL,
54952     getBox : function(){
54953         if(this.collapsed){
54954             return this.collapsedEl.getBox();
54955         }
54956         var box = this.el.getBox();
54957         if(this.split){
54958             var sh = this.split.el.getHeight();
54959             box.height += sh;
54960             box.y -= sh;
54961         }
54962         return box;
54963     },
54964     
54965     updateBox : function(box){
54966         if(this.split && !this.collapsed){
54967             var sh = this.split.el.getHeight();
54968             box.height -= sh;
54969             box.y += sh;
54970             this.split.el.setLeft(box.x);
54971             this.split.el.setTop(box.y-sh);
54972             this.split.el.setWidth(box.width);
54973         }
54974         if(this.collapsed){
54975             this.updateBody(box.width, null);
54976         }
54977         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54978     }
54979 });
54980
54981 Roo.EastLayoutRegion = function(mgr, config){
54982     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54983     if(this.split){
54984         this.split.placement = Roo.SplitBar.RIGHT;
54985         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54986         this.split.el.addClass("x-layout-split-h");
54987     }
54988     var size = config.initialSize || config.width;
54989     if(typeof size != "undefined"){
54990         this.el.setWidth(size);
54991     }
54992 };
54993 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54994     orientation: Roo.SplitBar.HORIZONTAL,
54995     getBox : function(){
54996         if(this.collapsed){
54997             return this.collapsedEl.getBox();
54998         }
54999         var box = this.el.getBox();
55000         if(this.split){
55001             var sw = this.split.el.getWidth();
55002             box.width += sw;
55003             box.x -= sw;
55004         }
55005         return box;
55006     },
55007
55008     updateBox : function(box){
55009         if(this.split && !this.collapsed){
55010             var sw = this.split.el.getWidth();
55011             box.width -= sw;
55012             this.split.el.setLeft(box.x);
55013             this.split.el.setTop(box.y);
55014             this.split.el.setHeight(box.height);
55015             box.x += sw;
55016         }
55017         if(this.collapsed){
55018             this.updateBody(null, box.height);
55019         }
55020         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55021     }
55022 });
55023
55024 Roo.WestLayoutRegion = function(mgr, config){
55025     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55026     if(this.split){
55027         this.split.placement = Roo.SplitBar.LEFT;
55028         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55029         this.split.el.addClass("x-layout-split-h");
55030     }
55031     var size = config.initialSize || config.width;
55032     if(typeof size != "undefined"){
55033         this.el.setWidth(size);
55034     }
55035 };
55036 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55037     orientation: Roo.SplitBar.HORIZONTAL,
55038     getBox : function(){
55039         if(this.collapsed){
55040             return this.collapsedEl.getBox();
55041         }
55042         var box = this.el.getBox();
55043         if(this.split){
55044             box.width += this.split.el.getWidth();
55045         }
55046         return box;
55047     },
55048     
55049     updateBox : function(box){
55050         if(this.split && !this.collapsed){
55051             var sw = this.split.el.getWidth();
55052             box.width -= sw;
55053             this.split.el.setLeft(box.x+box.width);
55054             this.split.el.setTop(box.y);
55055             this.split.el.setHeight(box.height);
55056         }
55057         if(this.collapsed){
55058             this.updateBody(null, box.height);
55059         }
55060         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55061     }
55062 });
55063 /*
55064  * Based on:
55065  * Ext JS Library 1.1.1
55066  * Copyright(c) 2006-2007, Ext JS, LLC.
55067  *
55068  * Originally Released Under LGPL - original licence link has changed is not relivant.
55069  *
55070  * Fork - LGPL
55071  * <script type="text/javascript">
55072  */
55073  
55074  
55075 /*
55076  * Private internal class for reading and applying state
55077  */
55078 Roo.LayoutStateManager = function(layout){
55079      // default empty state
55080      this.state = {
55081         north: {},
55082         south: {},
55083         east: {},
55084         west: {}       
55085     };
55086 };
55087
55088 Roo.LayoutStateManager.prototype = {
55089     init : function(layout, provider){
55090         this.provider = provider;
55091         var state = provider.get(layout.id+"-layout-state");
55092         if(state){
55093             var wasUpdating = layout.isUpdating();
55094             if(!wasUpdating){
55095                 layout.beginUpdate();
55096             }
55097             for(var key in state){
55098                 if(typeof state[key] != "function"){
55099                     var rstate = state[key];
55100                     var r = layout.getRegion(key);
55101                     if(r && rstate){
55102                         if(rstate.size){
55103                             r.resizeTo(rstate.size);
55104                         }
55105                         if(rstate.collapsed == true){
55106                             r.collapse(true);
55107                         }else{
55108                             r.expand(null, true);
55109                         }
55110                     }
55111                 }
55112             }
55113             if(!wasUpdating){
55114                 layout.endUpdate();
55115             }
55116             this.state = state; 
55117         }
55118         this.layout = layout;
55119         layout.on("regionresized", this.onRegionResized, this);
55120         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55121         layout.on("regionexpanded", this.onRegionExpanded, this);
55122     },
55123     
55124     storeState : function(){
55125         this.provider.set(this.layout.id+"-layout-state", this.state);
55126     },
55127     
55128     onRegionResized : function(region, newSize){
55129         this.state[region.getPosition()].size = newSize;
55130         this.storeState();
55131     },
55132     
55133     onRegionCollapsed : function(region){
55134         this.state[region.getPosition()].collapsed = true;
55135         this.storeState();
55136     },
55137     
55138     onRegionExpanded : function(region){
55139         this.state[region.getPosition()].collapsed = false;
55140         this.storeState();
55141     }
55142 };/*
55143  * Based on:
55144  * Ext JS Library 1.1.1
55145  * Copyright(c) 2006-2007, Ext JS, LLC.
55146  *
55147  * Originally Released Under LGPL - original licence link has changed is not relivant.
55148  *
55149  * Fork - LGPL
55150  * <script type="text/javascript">
55151  */
55152 /**
55153  * @class Roo.ContentPanel
55154  * @extends Roo.util.Observable
55155  * @children Roo.form.Form Roo.JsonView Roo.View
55156  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55157  * A basic ContentPanel element.
55158  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55159  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55160  * @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
55161  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55162  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55163  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55164  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55165  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55166  * @cfg {String} title          The title for this panel
55167  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55168  * @cfg {String} url            Calls {@link #setUrl} with this value
55169  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55170  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55171  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55172  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55173  * @cfg {String}    style  Extra style to add to the content panel
55174  * @cfg {Roo.menu.Menu} menu  popup menu
55175
55176  * @constructor
55177  * Create a new ContentPanel.
55178  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55179  * @param {String/Object} config A string to set only the title or a config object
55180  * @param {String} content (optional) Set the HTML content for this panel
55181  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55182  */
55183 Roo.ContentPanel = function(el, config, content){
55184     
55185      
55186     /*
55187     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55188         config = el;
55189         el = Roo.id();
55190     }
55191     if (config && config.parentLayout) { 
55192         el = config.parentLayout.el.createChild(); 
55193     }
55194     */
55195     if(el.autoCreate){ // xtype is available if this is called from factory
55196         config = el;
55197         el = Roo.id();
55198     }
55199     this.el = Roo.get(el);
55200     if(!this.el && config && config.autoCreate){
55201         if(typeof config.autoCreate == "object"){
55202             if(!config.autoCreate.id){
55203                 config.autoCreate.id = config.id||el;
55204             }
55205             this.el = Roo.DomHelper.append(document.body,
55206                         config.autoCreate, true);
55207         }else{
55208             this.el = Roo.DomHelper.append(document.body,
55209                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55210         }
55211     }
55212     
55213     
55214     this.closable = false;
55215     this.loaded = false;
55216     this.active = false;
55217     if(typeof config == "string"){
55218         this.title = config;
55219     }else{
55220         Roo.apply(this, config);
55221     }
55222     
55223     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55224         this.wrapEl = this.el.wrap();
55225         this.toolbar.container = this.el.insertSibling(false, 'before');
55226         this.toolbar = new Roo.Toolbar(this.toolbar);
55227     }
55228     
55229     // xtype created footer. - not sure if will work as we normally have to render first..
55230     if (this.footer && !this.footer.el && this.footer.xtype) {
55231         if (!this.wrapEl) {
55232             this.wrapEl = this.el.wrap();
55233         }
55234     
55235         this.footer.container = this.wrapEl.createChild();
55236          
55237         this.footer = Roo.factory(this.footer, Roo);
55238         
55239     }
55240     
55241     if(this.resizeEl){
55242         this.resizeEl = Roo.get(this.resizeEl, true);
55243     }else{
55244         this.resizeEl = this.el;
55245     }
55246     // handle view.xtype
55247     
55248  
55249     
55250     
55251     this.addEvents({
55252         /**
55253          * @event activate
55254          * Fires when this panel is activated. 
55255          * @param {Roo.ContentPanel} this
55256          */
55257         "activate" : true,
55258         /**
55259          * @event deactivate
55260          * Fires when this panel is activated. 
55261          * @param {Roo.ContentPanel} this
55262          */
55263         "deactivate" : true,
55264
55265         /**
55266          * @event resize
55267          * Fires when this panel is resized if fitToFrame is true.
55268          * @param {Roo.ContentPanel} this
55269          * @param {Number} width The width after any component adjustments
55270          * @param {Number} height The height after any component adjustments
55271          */
55272         "resize" : true,
55273         
55274          /**
55275          * @event render
55276          * Fires when this tab is created
55277          * @param {Roo.ContentPanel} this
55278          */
55279         "render" : true
55280          
55281         
55282     });
55283     
55284
55285     
55286     
55287     if(this.autoScroll){
55288         this.resizeEl.setStyle("overflow", "auto");
55289     } else {
55290         // fix randome scrolling
55291         this.el.on('scroll', function() {
55292             Roo.log('fix random scolling');
55293             this.scrollTo('top',0); 
55294         });
55295     }
55296     content = content || this.content;
55297     if(content){
55298         this.setContent(content);
55299     }
55300     if(config && config.url){
55301         this.setUrl(this.url, this.params, this.loadOnce);
55302     }
55303     
55304     
55305     
55306     Roo.ContentPanel.superclass.constructor.call(this);
55307     
55308     if (this.view && typeof(this.view.xtype) != 'undefined') {
55309         this.view.el = this.el.appendChild(document.createElement("div"));
55310         this.view = Roo.factory(this.view); 
55311         this.view.render  &&  this.view.render(false, '');  
55312     }
55313     
55314     
55315     this.fireEvent('render', this);
55316 };
55317
55318 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55319     tabTip:'',
55320     setRegion : function(region){
55321         this.region = region;
55322         if(region){
55323            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55324         }else{
55325            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55326         } 
55327     },
55328     
55329     /**
55330      * Returns the toolbar for this Panel if one was configured. 
55331      * @return {Roo.Toolbar} 
55332      */
55333     getToolbar : function(){
55334         return this.toolbar;
55335     },
55336     
55337     setActiveState : function(active){
55338         this.active = active;
55339         if(!active){
55340             this.fireEvent("deactivate", this);
55341         }else{
55342             this.fireEvent("activate", this);
55343         }
55344     },
55345     /**
55346      * Updates this panel's element
55347      * @param {String} content The new content
55348      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55349     */
55350     setContent : function(content, loadScripts){
55351         this.el.update(content, loadScripts);
55352     },
55353
55354     ignoreResize : function(w, h){
55355         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55356             return true;
55357         }else{
55358             this.lastSize = {width: w, height: h};
55359             return false;
55360         }
55361     },
55362     /**
55363      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55364      * @return {Roo.UpdateManager} The UpdateManager
55365      */
55366     getUpdateManager : function(){
55367         return this.el.getUpdateManager();
55368     },
55369      /**
55370      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55371      * @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:
55372 <pre><code>
55373 panel.load({
55374     url: "your-url.php",
55375     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55376     callback: yourFunction,
55377     scope: yourObject, //(optional scope)
55378     discardUrl: false,
55379     nocache: false,
55380     text: "Loading...",
55381     timeout: 30,
55382     scripts: false
55383 });
55384 </code></pre>
55385      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55386      * 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.
55387      * @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}
55388      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55389      * @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.
55390      * @return {Roo.ContentPanel} this
55391      */
55392     load : function(){
55393         var um = this.el.getUpdateManager();
55394         um.update.apply(um, arguments);
55395         return this;
55396     },
55397
55398
55399     /**
55400      * 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.
55401      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55402      * @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)
55403      * @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)
55404      * @return {Roo.UpdateManager} The UpdateManager
55405      */
55406     setUrl : function(url, params, loadOnce){
55407         if(this.refreshDelegate){
55408             this.removeListener("activate", this.refreshDelegate);
55409         }
55410         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55411         this.on("activate", this.refreshDelegate);
55412         return this.el.getUpdateManager();
55413     },
55414     
55415     _handleRefresh : function(url, params, loadOnce){
55416         if(!loadOnce || !this.loaded){
55417             var updater = this.el.getUpdateManager();
55418             updater.update(url, params, this._setLoaded.createDelegate(this));
55419         }
55420     },
55421     
55422     _setLoaded : function(){
55423         this.loaded = true;
55424     }, 
55425     
55426     /**
55427      * Returns this panel's id
55428      * @return {String} 
55429      */
55430     getId : function(){
55431         return this.el.id;
55432     },
55433     
55434     /** 
55435      * Returns this panel's element - used by regiosn to add.
55436      * @return {Roo.Element} 
55437      */
55438     getEl : function(){
55439         return this.wrapEl || this.el;
55440     },
55441     
55442     adjustForComponents : function(width, height)
55443     {
55444         //Roo.log('adjustForComponents ');
55445         if(this.resizeEl != this.el){
55446             width -= this.el.getFrameWidth('lr');
55447             height -= this.el.getFrameWidth('tb');
55448         }
55449         if(this.toolbar){
55450             var te = this.toolbar.getEl();
55451             height -= te.getHeight();
55452             te.setWidth(width);
55453         }
55454         if(this.footer){
55455             var te = this.footer.getEl();
55456             //Roo.log("footer:" + te.getHeight());
55457             
55458             height -= te.getHeight();
55459             te.setWidth(width);
55460         }
55461         
55462         
55463         if(this.adjustments){
55464             width += this.adjustments[0];
55465             height += this.adjustments[1];
55466         }
55467         return {"width": width, "height": height};
55468     },
55469     
55470     setSize : function(width, height){
55471         if(this.fitToFrame && !this.ignoreResize(width, height)){
55472             if(this.fitContainer && this.resizeEl != this.el){
55473                 this.el.setSize(width, height);
55474             }
55475             var size = this.adjustForComponents(width, height);
55476             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
55477             this.fireEvent('resize', this, size.width, size.height);
55478         }
55479     },
55480     
55481     /**
55482      * Returns this panel's title
55483      * @return {String} 
55484      */
55485     getTitle : function(){
55486         return this.title;
55487     },
55488     
55489     /**
55490      * Set this panel's title
55491      * @param {String} title
55492      */
55493     setTitle : function(title){
55494         this.title = title;
55495         if(this.region){
55496             this.region.updatePanelTitle(this, title);
55497         }
55498     },
55499     
55500     /**
55501      * Returns true is this panel was configured to be closable
55502      * @return {Boolean} 
55503      */
55504     isClosable : function(){
55505         return this.closable;
55506     },
55507     
55508     beforeSlide : function(){
55509         this.el.clip();
55510         this.resizeEl.clip();
55511     },
55512     
55513     afterSlide : function(){
55514         this.el.unclip();
55515         this.resizeEl.unclip();
55516     },
55517     
55518     /**
55519      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
55520      *   Will fail silently if the {@link #setUrl} method has not been called.
55521      *   This does not activate the panel, just updates its content.
55522      */
55523     refresh : function(){
55524         if(this.refreshDelegate){
55525            this.loaded = false;
55526            this.refreshDelegate();
55527         }
55528     },
55529     
55530     /**
55531      * Destroys this panel
55532      */
55533     destroy : function(){
55534         this.el.removeAllListeners();
55535         var tempEl = document.createElement("span");
55536         tempEl.appendChild(this.el.dom);
55537         tempEl.innerHTML = "";
55538         this.el.remove();
55539         this.el = null;
55540     },
55541     
55542     /**
55543      * form - if the content panel contains a form - this is a reference to it.
55544      * @type {Roo.form.Form}
55545      */
55546     form : false,
55547     /**
55548      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
55549      *    This contains a reference to it.
55550      * @type {Roo.View}
55551      */
55552     view : false,
55553     
55554       /**
55555      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
55556      * <pre><code>
55557
55558 layout.addxtype({
55559        xtype : 'Form',
55560        items: [ .... ]
55561    }
55562 );
55563
55564 </code></pre>
55565      * @param {Object} cfg Xtype definition of item to add.
55566      */
55567     
55568     addxtype : function(cfg) {
55569         // add form..
55570         if (cfg.xtype.match(/^Form$/)) {
55571             
55572             var el;
55573             //if (this.footer) {
55574             //    el = this.footer.container.insertSibling(false, 'before');
55575             //} else {
55576                 el = this.el.createChild();
55577             //}
55578
55579             this.form = new  Roo.form.Form(cfg);
55580             
55581             
55582             if ( this.form.allItems.length) {
55583                 this.form.render(el.dom);
55584             }
55585             return this.form;
55586         }
55587         // should only have one of theses..
55588         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
55589             // views.. should not be just added - used named prop 'view''
55590             
55591             cfg.el = this.el.appendChild(document.createElement("div"));
55592             // factory?
55593             
55594             var ret = new Roo.factory(cfg);
55595              
55596              ret.render && ret.render(false, ''); // render blank..
55597             this.view = ret;
55598             return ret;
55599         }
55600         return false;
55601     }
55602 });
55603
55604 /**
55605  * @class Roo.GridPanel
55606  * @extends Roo.ContentPanel
55607  * @constructor
55608  * Create a new GridPanel.
55609  * @param {Roo.grid.Grid} grid The grid for this panel
55610  * @param {String/Object} config A string to set only the panel's title, or a config object
55611  */
55612 Roo.GridPanel = function(grid, config){
55613     
55614   
55615     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
55616         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
55617         
55618     this.wrapper.dom.appendChild(grid.getGridEl().dom);
55619     
55620     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
55621     
55622     if(this.toolbar){
55623         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
55624     }
55625     // xtype created footer. - not sure if will work as we normally have to render first..
55626     if (this.footer && !this.footer.el && this.footer.xtype) {
55627         
55628         this.footer.container = this.grid.getView().getFooterPanel(true);
55629         this.footer.dataSource = this.grid.dataSource;
55630         this.footer = Roo.factory(this.footer, Roo);
55631         
55632     }
55633     
55634     grid.monitorWindowResize = false; // turn off autosizing
55635     grid.autoHeight = false;
55636     grid.autoWidth = false;
55637     this.grid = grid;
55638     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
55639 };
55640
55641 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
55642     getId : function(){
55643         return this.grid.id;
55644     },
55645     
55646     /**
55647      * Returns the grid for this panel
55648      * @return {Roo.grid.Grid} 
55649      */
55650     getGrid : function(){
55651         return this.grid;    
55652     },
55653     
55654     setSize : function(width, height){
55655         if(!this.ignoreResize(width, height)){
55656             var grid = this.grid;
55657             var size = this.adjustForComponents(width, height);
55658             grid.getGridEl().setSize(size.width, size.height);
55659             grid.autoSize();
55660         }
55661     },
55662     
55663     beforeSlide : function(){
55664         this.grid.getView().scroller.clip();
55665     },
55666     
55667     afterSlide : function(){
55668         this.grid.getView().scroller.unclip();
55669     },
55670     
55671     destroy : function(){
55672         this.grid.destroy();
55673         delete this.grid;
55674         Roo.GridPanel.superclass.destroy.call(this); 
55675     }
55676 });
55677
55678
55679 /**
55680  * @class Roo.NestedLayoutPanel
55681  * @extends Roo.ContentPanel
55682  * @constructor
55683  * Create a new NestedLayoutPanel.
55684  * 
55685  * 
55686  * @param {Roo.BorderLayout} layout [required] The layout for this panel
55687  * @param {String/Object} config A string to set only the title or a config object
55688  */
55689 Roo.NestedLayoutPanel = function(layout, config)
55690 {
55691     // construct with only one argument..
55692     /* FIXME - implement nicer consturctors
55693     if (layout.layout) {
55694         config = layout;
55695         layout = config.layout;
55696         delete config.layout;
55697     }
55698     if (layout.xtype && !layout.getEl) {
55699         // then layout needs constructing..
55700         layout = Roo.factory(layout, Roo);
55701     }
55702     */
55703     
55704     
55705     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
55706     
55707     layout.monitorWindowResize = false; // turn off autosizing
55708     this.layout = layout;
55709     this.layout.getEl().addClass("x-layout-nested-layout");
55710     
55711     
55712     
55713     
55714 };
55715
55716 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
55717
55718     setSize : function(width, height){
55719         if(!this.ignoreResize(width, height)){
55720             var size = this.adjustForComponents(width, height);
55721             var el = this.layout.getEl();
55722             el.setSize(size.width, size.height);
55723             var touch = el.dom.offsetWidth;
55724             this.layout.layout();
55725             // ie requires a double layout on the first pass
55726             if(Roo.isIE && !this.initialized){
55727                 this.initialized = true;
55728                 this.layout.layout();
55729             }
55730         }
55731     },
55732     
55733     // activate all subpanels if not currently active..
55734     
55735     setActiveState : function(active){
55736         this.active = active;
55737         if(!active){
55738             this.fireEvent("deactivate", this);
55739             return;
55740         }
55741         
55742         this.fireEvent("activate", this);
55743         // not sure if this should happen before or after..
55744         if (!this.layout) {
55745             return; // should not happen..
55746         }
55747         var reg = false;
55748         for (var r in this.layout.regions) {
55749             reg = this.layout.getRegion(r);
55750             if (reg.getActivePanel()) {
55751                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55752                 reg.setActivePanel(reg.getActivePanel());
55753                 continue;
55754             }
55755             if (!reg.panels.length) {
55756                 continue;
55757             }
55758             reg.showPanel(reg.getPanel(0));
55759         }
55760         
55761         
55762         
55763         
55764     },
55765     
55766     /**
55767      * Returns the nested BorderLayout for this panel
55768      * @return {Roo.BorderLayout} 
55769      */
55770     getLayout : function(){
55771         return this.layout;
55772     },
55773     
55774      /**
55775      * Adds a xtype elements to the layout of the nested panel
55776      * <pre><code>
55777
55778 panel.addxtype({
55779        xtype : 'ContentPanel',
55780        region: 'west',
55781        items: [ .... ]
55782    }
55783 );
55784
55785 panel.addxtype({
55786         xtype : 'NestedLayoutPanel',
55787         region: 'west',
55788         layout: {
55789            center: { },
55790            west: { }   
55791         },
55792         items : [ ... list of content panels or nested layout panels.. ]
55793    }
55794 );
55795 </code></pre>
55796      * @param {Object} cfg Xtype definition of item to add.
55797      */
55798     addxtype : function(cfg) {
55799         return this.layout.addxtype(cfg);
55800     
55801     }
55802 });
55803
55804 Roo.ScrollPanel = function(el, config, content){
55805     config = config || {};
55806     config.fitToFrame = true;
55807     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55808     
55809     this.el.dom.style.overflow = "hidden";
55810     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55811     this.el.removeClass("x-layout-inactive-content");
55812     this.el.on("mousewheel", this.onWheel, this);
55813
55814     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55815     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55816     up.unselectable(); down.unselectable();
55817     up.on("click", this.scrollUp, this);
55818     down.on("click", this.scrollDown, this);
55819     up.addClassOnOver("x-scroller-btn-over");
55820     down.addClassOnOver("x-scroller-btn-over");
55821     up.addClassOnClick("x-scroller-btn-click");
55822     down.addClassOnClick("x-scroller-btn-click");
55823     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55824
55825     this.resizeEl = this.el;
55826     this.el = wrap; this.up = up; this.down = down;
55827 };
55828
55829 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55830     increment : 100,
55831     wheelIncrement : 5,
55832     scrollUp : function(){
55833         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55834     },
55835
55836     scrollDown : function(){
55837         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55838     },
55839
55840     afterScroll : function(){
55841         var el = this.resizeEl;
55842         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55843         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55844         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55845     },
55846
55847     setSize : function(){
55848         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55849         this.afterScroll();
55850     },
55851
55852     onWheel : function(e){
55853         var d = e.getWheelDelta();
55854         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55855         this.afterScroll();
55856         e.stopEvent();
55857     },
55858
55859     setContent : function(content, loadScripts){
55860         this.resizeEl.update(content, loadScripts);
55861     }
55862
55863 });
55864
55865
55866
55867 /**
55868  * @class Roo.TreePanel
55869  * @extends Roo.ContentPanel
55870  * Treepanel component
55871  * 
55872  * @constructor
55873  * Create a new TreePanel. - defaults to fit/scoll contents.
55874  * @param {String/Object} config A string to set only the panel's title, or a config object
55875  */
55876 Roo.TreePanel = function(config){
55877     var el = config.el;
55878     var tree = config.tree;
55879     delete config.tree; 
55880     delete config.el; // hopefull!
55881     
55882     // wrapper for IE7 strict & safari scroll issue
55883     
55884     var treeEl = el.createChild();
55885     config.resizeEl = treeEl;
55886     
55887     
55888     
55889     Roo.TreePanel.superclass.constructor.call(this, el, config);
55890  
55891  
55892     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55893     //console.log(tree);
55894     this.on('activate', function()
55895     {
55896         if (this.tree.rendered) {
55897             return;
55898         }
55899         //console.log('render tree');
55900         this.tree.render();
55901     });
55902     // this should not be needed.. - it's actually the 'el' that resizes?
55903     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55904     
55905     //this.on('resize',  function (cp, w, h) {
55906     //        this.tree.innerCt.setWidth(w);
55907     //        this.tree.innerCt.setHeight(h);
55908     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55909     //});
55910
55911         
55912     
55913 };
55914
55915 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55916     fitToFrame : true,
55917     autoScroll : true,
55918     /*
55919      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
55920      */
55921     tree : false
55922
55923 });
55924
55925
55926
55927
55928
55929
55930
55931
55932
55933
55934
55935 /*
55936  * Based on:
55937  * Ext JS Library 1.1.1
55938  * Copyright(c) 2006-2007, Ext JS, LLC.
55939  *
55940  * Originally Released Under LGPL - original licence link has changed is not relivant.
55941  *
55942  * Fork - LGPL
55943  * <script type="text/javascript">
55944  */
55945  
55946
55947 /**
55948  * @class Roo.ReaderLayout
55949  * @extends Roo.BorderLayout
55950  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55951  * center region containing two nested regions (a top one for a list view and one for item preview below),
55952  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55953  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55954  * expedites the setup of the overall layout and regions for this common application style.
55955  * Example:
55956  <pre><code>
55957 var reader = new Roo.ReaderLayout();
55958 var CP = Roo.ContentPanel;  // shortcut for adding
55959
55960 reader.beginUpdate();
55961 reader.add("north", new CP("north", "North"));
55962 reader.add("west", new CP("west", {title: "West"}));
55963 reader.add("east", new CP("east", {title: "East"}));
55964
55965 reader.regions.listView.add(new CP("listView", "List"));
55966 reader.regions.preview.add(new CP("preview", "Preview"));
55967 reader.endUpdate();
55968 </code></pre>
55969 * @constructor
55970 * Create a new ReaderLayout
55971 * @param {Object} config Configuration options
55972 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55973 * document.body if omitted)
55974 */
55975 Roo.ReaderLayout = function(config, renderTo){
55976     var c = config || {size:{}};
55977     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55978         north: c.north !== false ? Roo.apply({
55979             split:false,
55980             initialSize: 32,
55981             titlebar: false
55982         }, c.north) : false,
55983         west: c.west !== false ? Roo.apply({
55984             split:true,
55985             initialSize: 200,
55986             minSize: 175,
55987             maxSize: 400,
55988             titlebar: true,
55989             collapsible: true,
55990             animate: true,
55991             margins:{left:5,right:0,bottom:5,top:5},
55992             cmargins:{left:5,right:5,bottom:5,top:5}
55993         }, c.west) : false,
55994         east: c.east !== false ? Roo.apply({
55995             split:true,
55996             initialSize: 200,
55997             minSize: 175,
55998             maxSize: 400,
55999             titlebar: true,
56000             collapsible: true,
56001             animate: true,
56002             margins:{left:0,right:5,bottom:5,top:5},
56003             cmargins:{left:5,right:5,bottom:5,top:5}
56004         }, c.east) : false,
56005         center: Roo.apply({
56006             tabPosition: 'top',
56007             autoScroll:false,
56008             closeOnTab: true,
56009             titlebar:false,
56010             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56011         }, c.center)
56012     });
56013
56014     this.el.addClass('x-reader');
56015
56016     this.beginUpdate();
56017
56018     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56019         south: c.preview !== false ? Roo.apply({
56020             split:true,
56021             initialSize: 200,
56022             minSize: 100,
56023             autoScroll:true,
56024             collapsible:true,
56025             titlebar: true,
56026             cmargins:{top:5,left:0, right:0, bottom:0}
56027         }, c.preview) : false,
56028         center: Roo.apply({
56029             autoScroll:false,
56030             titlebar:false,
56031             minHeight:200
56032         }, c.listView)
56033     });
56034     this.add('center', new Roo.NestedLayoutPanel(inner,
56035             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56036
56037     this.endUpdate();
56038
56039     this.regions.preview = inner.getRegion('south');
56040     this.regions.listView = inner.getRegion('center');
56041 };
56042
56043 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56044  * Based on:
56045  * Ext JS Library 1.1.1
56046  * Copyright(c) 2006-2007, Ext JS, LLC.
56047  *
56048  * Originally Released Under LGPL - original licence link has changed is not relivant.
56049  *
56050  * Fork - LGPL
56051  * <script type="text/javascript">
56052  */
56053  
56054 /**
56055  * @class Roo.grid.Grid
56056  * @extends Roo.util.Observable
56057  * This class represents the primary interface of a component based grid control.
56058  * <br><br>Usage:<pre><code>
56059  var grid = new Roo.grid.Grid("my-container-id", {
56060      ds: myDataStore,
56061      cm: myColModel,
56062      selModel: mySelectionModel,
56063      autoSizeColumns: true,
56064      monitorWindowResize: false,
56065      trackMouseOver: true
56066  });
56067  // set any options
56068  grid.render();
56069  * </code></pre>
56070  * <b>Common Problems:</b><br/>
56071  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56072  * element will correct this<br/>
56073  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56074  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56075  * are unpredictable.<br/>
56076  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56077  * grid to calculate dimensions/offsets.<br/>
56078   * @constructor
56079  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56080  * The container MUST have some type of size defined for the grid to fill. The container will be
56081  * automatically set to position relative if it isn't already.
56082  * @param {Object} config A config object that sets properties on this grid.
56083  */
56084 Roo.grid.Grid = function(container, config){
56085         // initialize the container
56086         this.container = Roo.get(container);
56087         this.container.update("");
56088         this.container.setStyle("overflow", "hidden");
56089     this.container.addClass('x-grid-container');
56090
56091     this.id = this.container.id;
56092
56093     Roo.apply(this, config);
56094     // check and correct shorthanded configs
56095     if(this.ds){
56096         this.dataSource = this.ds;
56097         delete this.ds;
56098     }
56099     if(this.cm){
56100         this.colModel = this.cm;
56101         delete this.cm;
56102     }
56103     if(this.sm){
56104         this.selModel = this.sm;
56105         delete this.sm;
56106     }
56107
56108     if (this.selModel) {
56109         this.selModel = Roo.factory(this.selModel, Roo.grid);
56110         this.sm = this.selModel;
56111         this.sm.xmodule = this.xmodule || false;
56112     }
56113     if (typeof(this.colModel.config) == 'undefined') {
56114         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56115         this.cm = this.colModel;
56116         this.cm.xmodule = this.xmodule || false;
56117     }
56118     if (this.dataSource) {
56119         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56120         this.ds = this.dataSource;
56121         this.ds.xmodule = this.xmodule || false;
56122          
56123     }
56124     
56125     
56126     
56127     if(this.width){
56128         this.container.setWidth(this.width);
56129     }
56130
56131     if(this.height){
56132         this.container.setHeight(this.height);
56133     }
56134     /** @private */
56135         this.addEvents({
56136         // raw events
56137         /**
56138          * @event click
56139          * The raw click event for the entire grid.
56140          * @param {Roo.EventObject} e
56141          */
56142         "click" : true,
56143         /**
56144          * @event dblclick
56145          * The raw dblclick event for the entire grid.
56146          * @param {Roo.EventObject} e
56147          */
56148         "dblclick" : true,
56149         /**
56150          * @event contextmenu
56151          * The raw contextmenu event for the entire grid.
56152          * @param {Roo.EventObject} e
56153          */
56154         "contextmenu" : true,
56155         /**
56156          * @event mousedown
56157          * The raw mousedown event for the entire grid.
56158          * @param {Roo.EventObject} e
56159          */
56160         "mousedown" : true,
56161         /**
56162          * @event mouseup
56163          * The raw mouseup event for the entire grid.
56164          * @param {Roo.EventObject} e
56165          */
56166         "mouseup" : true,
56167         /**
56168          * @event mouseover
56169          * The raw mouseover event for the entire grid.
56170          * @param {Roo.EventObject} e
56171          */
56172         "mouseover" : true,
56173         /**
56174          * @event mouseout
56175          * The raw mouseout event for the entire grid.
56176          * @param {Roo.EventObject} e
56177          */
56178         "mouseout" : true,
56179         /**
56180          * @event keypress
56181          * The raw keypress event for the entire grid.
56182          * @param {Roo.EventObject} e
56183          */
56184         "keypress" : true,
56185         /**
56186          * @event keydown
56187          * The raw keydown event for the entire grid.
56188          * @param {Roo.EventObject} e
56189          */
56190         "keydown" : true,
56191
56192         // custom events
56193
56194         /**
56195          * @event cellclick
56196          * Fires when a cell is clicked
56197          * @param {Grid} this
56198          * @param {Number} rowIndex
56199          * @param {Number} columnIndex
56200          * @param {Roo.EventObject} e
56201          */
56202         "cellclick" : true,
56203         /**
56204          * @event celldblclick
56205          * Fires when a cell is double clicked
56206          * @param {Grid} this
56207          * @param {Number} rowIndex
56208          * @param {Number} columnIndex
56209          * @param {Roo.EventObject} e
56210          */
56211         "celldblclick" : true,
56212         /**
56213          * @event rowclick
56214          * Fires when a row is clicked
56215          * @param {Grid} this
56216          * @param {Number} rowIndex
56217          * @param {Roo.EventObject} e
56218          */
56219         "rowclick" : true,
56220         /**
56221          * @event rowdblclick
56222          * Fires when a row is double clicked
56223          * @param {Grid} this
56224          * @param {Number} rowIndex
56225          * @param {Roo.EventObject} e
56226          */
56227         "rowdblclick" : true,
56228         /**
56229          * @event headerclick
56230          * Fires when a header is clicked
56231          * @param {Grid} this
56232          * @param {Number} columnIndex
56233          * @param {Roo.EventObject} e
56234          */
56235         "headerclick" : true,
56236         /**
56237          * @event headerdblclick
56238          * Fires when a header cell is double clicked
56239          * @param {Grid} this
56240          * @param {Number} columnIndex
56241          * @param {Roo.EventObject} e
56242          */
56243         "headerdblclick" : true,
56244         /**
56245          * @event rowcontextmenu
56246          * Fires when a row is right clicked
56247          * @param {Grid} this
56248          * @param {Number} rowIndex
56249          * @param {Roo.EventObject} e
56250          */
56251         "rowcontextmenu" : true,
56252         /**
56253          * @event cellcontextmenu
56254          * Fires when a cell is right clicked
56255          * @param {Grid} this
56256          * @param {Number} rowIndex
56257          * @param {Number} cellIndex
56258          * @param {Roo.EventObject} e
56259          */
56260          "cellcontextmenu" : true,
56261         /**
56262          * @event headercontextmenu
56263          * Fires when a header is right clicked
56264          * @param {Grid} this
56265          * @param {Number} columnIndex
56266          * @param {Roo.EventObject} e
56267          */
56268         "headercontextmenu" : true,
56269         /**
56270          * @event bodyscroll
56271          * Fires when the body element is scrolled
56272          * @param {Number} scrollLeft
56273          * @param {Number} scrollTop
56274          */
56275         "bodyscroll" : true,
56276         /**
56277          * @event columnresize
56278          * Fires when the user resizes a column
56279          * @param {Number} columnIndex
56280          * @param {Number} newSize
56281          */
56282         "columnresize" : true,
56283         /**
56284          * @event columnmove
56285          * Fires when the user moves a column
56286          * @param {Number} oldIndex
56287          * @param {Number} newIndex
56288          */
56289         "columnmove" : true,
56290         /**
56291          * @event startdrag
56292          * Fires when row(s) start being dragged
56293          * @param {Grid} this
56294          * @param {Roo.GridDD} dd The drag drop object
56295          * @param {event} e The raw browser event
56296          */
56297         "startdrag" : true,
56298         /**
56299          * @event enddrag
56300          * Fires when a drag operation is complete
56301          * @param {Grid} this
56302          * @param {Roo.GridDD} dd The drag drop object
56303          * @param {event} e The raw browser event
56304          */
56305         "enddrag" : true,
56306         /**
56307          * @event dragdrop
56308          * Fires when dragged row(s) are dropped on a valid DD target
56309          * @param {Grid} this
56310          * @param {Roo.GridDD} dd The drag drop object
56311          * @param {String} targetId The target drag drop object
56312          * @param {event} e The raw browser event
56313          */
56314         "dragdrop" : true,
56315         /**
56316          * @event dragover
56317          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56318          * @param {Grid} this
56319          * @param {Roo.GridDD} dd The drag drop object
56320          * @param {String} targetId The target drag drop object
56321          * @param {event} e The raw browser event
56322          */
56323         "dragover" : true,
56324         /**
56325          * @event dragenter
56326          *  Fires when the dragged row(s) first cross another DD target while being dragged
56327          * @param {Grid} this
56328          * @param {Roo.GridDD} dd The drag drop object
56329          * @param {String} targetId The target drag drop object
56330          * @param {event} e The raw browser event
56331          */
56332         "dragenter" : true,
56333         /**
56334          * @event dragout
56335          * Fires when the dragged row(s) leave another DD target while being dragged
56336          * @param {Grid} this
56337          * @param {Roo.GridDD} dd The drag drop object
56338          * @param {String} targetId The target drag drop object
56339          * @param {event} e The raw browser event
56340          */
56341         "dragout" : true,
56342         /**
56343          * @event rowclass
56344          * Fires when a row is rendered, so you can change add a style to it.
56345          * @param {GridView} gridview   The grid view
56346          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56347          */
56348         'rowclass' : true,
56349
56350         /**
56351          * @event render
56352          * Fires when the grid is rendered
56353          * @param {Grid} grid
56354          */
56355         'render' : true
56356     });
56357
56358     Roo.grid.Grid.superclass.constructor.call(this);
56359 };
56360 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56361     
56362     /**
56363          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56364          */
56365         /**
56366          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56367          */
56368         /**
56369          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56370          */
56371         /**
56372          * @cfg {Roo.grid.Store} ds The data store for the grid
56373          */
56374         /**
56375          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56376          */
56377         /**
56378      * @cfg {String} ddGroup - drag drop group.
56379      */
56380       /**
56381      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56382      */
56383
56384     /**
56385      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56386      */
56387     minColumnWidth : 25,
56388
56389     /**
56390      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56391      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56392      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56393      */
56394     autoSizeColumns : false,
56395
56396     /**
56397      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56398      */
56399     autoSizeHeaders : true,
56400
56401     /**
56402      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56403      */
56404     monitorWindowResize : true,
56405
56406     /**
56407      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56408      * rows measured to get a columns size. Default is 0 (all rows).
56409      */
56410     maxRowsToMeasure : 0,
56411
56412     /**
56413      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56414      */
56415     trackMouseOver : true,
56416
56417     /**
56418     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56419     */
56420       /**
56421     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56422     */
56423     
56424     /**
56425     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56426     */
56427     enableDragDrop : false,
56428     
56429     /**
56430     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56431     */
56432     enableColumnMove : true,
56433     
56434     /**
56435     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56436     */
56437     enableColumnHide : true,
56438     
56439     /**
56440     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56441     */
56442     enableRowHeightSync : false,
56443     
56444     /**
56445     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56446     */
56447     stripeRows : true,
56448     
56449     /**
56450     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
56451     */
56452     autoHeight : false,
56453
56454     /**
56455      * @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.
56456      */
56457     autoExpandColumn : false,
56458
56459     /**
56460     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
56461     * Default is 50.
56462     */
56463     autoExpandMin : 50,
56464
56465     /**
56466     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
56467     */
56468     autoExpandMax : 1000,
56469
56470     /**
56471     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
56472     */
56473     view : null,
56474
56475     /**
56476     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
56477     */
56478     loadMask : false,
56479     /**
56480     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
56481     */
56482     dropTarget: false,
56483     
56484    
56485     
56486     // private
56487     rendered : false,
56488
56489     /**
56490     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
56491     * of a fixed width. Default is false.
56492     */
56493     /**
56494     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
56495     */
56496     
56497     
56498     /**
56499     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
56500     * %0 is replaced with the number of selected rows.
56501     */
56502     ddText : "{0} selected row{1}",
56503     
56504     
56505     /**
56506      * Called once after all setup has been completed and the grid is ready to be rendered.
56507      * @return {Roo.grid.Grid} this
56508      */
56509     render : function()
56510     {
56511         var c = this.container;
56512         // try to detect autoHeight/width mode
56513         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
56514             this.autoHeight = true;
56515         }
56516         var view = this.getView();
56517         view.init(this);
56518
56519         c.on("click", this.onClick, this);
56520         c.on("dblclick", this.onDblClick, this);
56521         c.on("contextmenu", this.onContextMenu, this);
56522         c.on("keydown", this.onKeyDown, this);
56523         if (Roo.isTouch) {
56524             c.on("touchstart", this.onTouchStart, this);
56525         }
56526
56527         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
56528
56529         this.getSelectionModel().init(this);
56530
56531         view.render();
56532
56533         if(this.loadMask){
56534             this.loadMask = new Roo.LoadMask(this.container,
56535                     Roo.apply({store:this.dataSource}, this.loadMask));
56536         }
56537         
56538         
56539         if (this.toolbar && this.toolbar.xtype) {
56540             this.toolbar.container = this.getView().getHeaderPanel(true);
56541             this.toolbar = new Roo.Toolbar(this.toolbar);
56542         }
56543         if (this.footer && this.footer.xtype) {
56544             this.footer.dataSource = this.getDataSource();
56545             this.footer.container = this.getView().getFooterPanel(true);
56546             this.footer = Roo.factory(this.footer, Roo);
56547         }
56548         if (this.dropTarget && this.dropTarget.xtype) {
56549             delete this.dropTarget.xtype;
56550             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
56551         }
56552         
56553         
56554         this.rendered = true;
56555         this.fireEvent('render', this);
56556         return this;
56557     },
56558
56559     /**
56560      * Reconfigures the grid to use a different Store and Column Model.
56561      * The View will be bound to the new objects and refreshed.
56562      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
56563      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
56564      */
56565     reconfigure : function(dataSource, colModel){
56566         if(this.loadMask){
56567             this.loadMask.destroy();
56568             this.loadMask = new Roo.LoadMask(this.container,
56569                     Roo.apply({store:dataSource}, this.loadMask));
56570         }
56571         this.view.bind(dataSource, colModel);
56572         this.dataSource = dataSource;
56573         this.colModel = colModel;
56574         this.view.refresh(true);
56575     },
56576     /**
56577      * addColumns
56578      * Add's a column, default at the end..
56579      
56580      * @param {int} position to add (default end)
56581      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
56582      */
56583     addColumns : function(pos, ar)
56584     {
56585         
56586         for (var i =0;i< ar.length;i++) {
56587             var cfg = ar[i];
56588             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
56589             this.cm.lookup[cfg.id] = cfg;
56590         }
56591         
56592         
56593         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
56594             pos = this.cm.config.length; //this.cm.config.push(cfg);
56595         } 
56596         pos = Math.max(0,pos);
56597         ar.unshift(0);
56598         ar.unshift(pos);
56599         this.cm.config.splice.apply(this.cm.config, ar);
56600         
56601         
56602         
56603         this.view.generateRules(this.cm);
56604         this.view.refresh(true);
56605         
56606     },
56607     
56608     
56609     
56610     
56611     // private
56612     onKeyDown : function(e){
56613         this.fireEvent("keydown", e);
56614     },
56615
56616     /**
56617      * Destroy this grid.
56618      * @param {Boolean} removeEl True to remove the element
56619      */
56620     destroy : function(removeEl, keepListeners){
56621         if(this.loadMask){
56622             this.loadMask.destroy();
56623         }
56624         var c = this.container;
56625         c.removeAllListeners();
56626         this.view.destroy();
56627         this.colModel.purgeListeners();
56628         if(!keepListeners){
56629             this.purgeListeners();
56630         }
56631         c.update("");
56632         if(removeEl === true){
56633             c.remove();
56634         }
56635     },
56636
56637     // private
56638     processEvent : function(name, e){
56639         // does this fire select???
56640         //Roo.log('grid:processEvent '  + name);
56641         
56642         if (name != 'touchstart' ) {
56643             this.fireEvent(name, e);    
56644         }
56645         
56646         var t = e.getTarget();
56647         var v = this.view;
56648         var header = v.findHeaderIndex(t);
56649         if(header !== false){
56650             var ename = name == 'touchstart' ? 'click' : name;
56651              
56652             this.fireEvent("header" + ename, this, header, e);
56653         }else{
56654             var row = v.findRowIndex(t);
56655             var cell = v.findCellIndex(t);
56656             if (name == 'touchstart') {
56657                 // first touch is always a click.
56658                 // hopefull this happens after selection is updated.?
56659                 name = false;
56660                 
56661                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
56662                     var cs = this.selModel.getSelectedCell();
56663                     if (row == cs[0] && cell == cs[1]){
56664                         name = 'dblclick';
56665                     }
56666                 }
56667                 if (typeof(this.selModel.getSelections) != 'undefined') {
56668                     var cs = this.selModel.getSelections();
56669                     var ds = this.dataSource;
56670                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
56671                         name = 'dblclick';
56672                     }
56673                 }
56674                 if (!name) {
56675                     return;
56676                 }
56677             }
56678             
56679             
56680             if(row !== false){
56681                 this.fireEvent("row" + name, this, row, e);
56682                 if(cell !== false){
56683                     this.fireEvent("cell" + name, this, row, cell, e);
56684                 }
56685             }
56686         }
56687     },
56688
56689     // private
56690     onClick : function(e){
56691         this.processEvent("click", e);
56692     },
56693    // private
56694     onTouchStart : function(e){
56695         this.processEvent("touchstart", e);
56696     },
56697
56698     // private
56699     onContextMenu : function(e, t){
56700         this.processEvent("contextmenu", e);
56701     },
56702
56703     // private
56704     onDblClick : function(e){
56705         this.processEvent("dblclick", e);
56706     },
56707
56708     // private
56709     walkCells : function(row, col, step, fn, scope){
56710         var cm = this.colModel, clen = cm.getColumnCount();
56711         var ds = this.dataSource, rlen = ds.getCount(), first = true;
56712         if(step < 0){
56713             if(col < 0){
56714                 row--;
56715                 first = false;
56716             }
56717             while(row >= 0){
56718                 if(!first){
56719                     col = clen-1;
56720                 }
56721                 first = false;
56722                 while(col >= 0){
56723                     if(fn.call(scope || this, row, col, cm) === true){
56724                         return [row, col];
56725                     }
56726                     col--;
56727                 }
56728                 row--;
56729             }
56730         } else {
56731             if(col >= clen){
56732                 row++;
56733                 first = false;
56734             }
56735             while(row < rlen){
56736                 if(!first){
56737                     col = 0;
56738                 }
56739                 first = false;
56740                 while(col < clen){
56741                     if(fn.call(scope || this, row, col, cm) === true){
56742                         return [row, col];
56743                     }
56744                     col++;
56745                 }
56746                 row++;
56747             }
56748         }
56749         return null;
56750     },
56751
56752     // private
56753     getSelections : function(){
56754         return this.selModel.getSelections();
56755     },
56756
56757     /**
56758      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56759      * but if manual update is required this method will initiate it.
56760      */
56761     autoSize : function(){
56762         if(this.rendered){
56763             this.view.layout();
56764             if(this.view.adjustForScroll){
56765                 this.view.adjustForScroll();
56766             }
56767         }
56768     },
56769
56770     /**
56771      * Returns the grid's underlying element.
56772      * @return {Element} The element
56773      */
56774     getGridEl : function(){
56775         return this.container;
56776     },
56777
56778     // private for compatibility, overridden by editor grid
56779     stopEditing : function(){},
56780
56781     /**
56782      * Returns the grid's SelectionModel.
56783      * @return {SelectionModel}
56784      */
56785     getSelectionModel : function(){
56786         if(!this.selModel){
56787             this.selModel = new Roo.grid.RowSelectionModel();
56788         }
56789         return this.selModel;
56790     },
56791
56792     /**
56793      * Returns the grid's DataSource.
56794      * @return {DataSource}
56795      */
56796     getDataSource : function(){
56797         return this.dataSource;
56798     },
56799
56800     /**
56801      * Returns the grid's ColumnModel.
56802      * @return {ColumnModel}
56803      */
56804     getColumnModel : function(){
56805         return this.colModel;
56806     },
56807
56808     /**
56809      * Returns the grid's GridView object.
56810      * @return {GridView}
56811      */
56812     getView : function(){
56813         if(!this.view){
56814             this.view = new Roo.grid.GridView(this.viewConfig);
56815             this.relayEvents(this.view, [
56816                 "beforerowremoved", "beforerowsinserted",
56817                 "beforerefresh", "rowremoved",
56818                 "rowsinserted", "rowupdated" ,"refresh"
56819             ]);
56820         }
56821         return this.view;
56822     },
56823     /**
56824      * Called to get grid's drag proxy text, by default returns this.ddText.
56825      * Override this to put something different in the dragged text.
56826      * @return {String}
56827      */
56828     getDragDropText : function(){
56829         var count = this.selModel.getCount();
56830         return String.format(this.ddText, count, count == 1 ? '' : 's');
56831     }
56832 });
56833 /*
56834  * Based on:
56835  * Ext JS Library 1.1.1
56836  * Copyright(c) 2006-2007, Ext JS, LLC.
56837  *
56838  * Originally Released Under LGPL - original licence link has changed is not relivant.
56839  *
56840  * Fork - LGPL
56841  * <script type="text/javascript">
56842  */
56843  /**
56844  * @class Roo.grid.AbstractGridView
56845  * @extends Roo.util.Observable
56846  * @abstract
56847  * Abstract base class for grid Views
56848  * @constructor
56849  */
56850 Roo.grid.AbstractGridView = function(){
56851         this.grid = null;
56852         
56853         this.events = {
56854             "beforerowremoved" : true,
56855             "beforerowsinserted" : true,
56856             "beforerefresh" : true,
56857             "rowremoved" : true,
56858             "rowsinserted" : true,
56859             "rowupdated" : true,
56860             "refresh" : true
56861         };
56862     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56863 };
56864
56865 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56866     rowClass : "x-grid-row",
56867     cellClass : "x-grid-cell",
56868     tdClass : "x-grid-td",
56869     hdClass : "x-grid-hd",
56870     splitClass : "x-grid-hd-split",
56871     
56872     init: function(grid){
56873         this.grid = grid;
56874                 var cid = this.grid.getGridEl().id;
56875         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56876         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56877         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56878         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56879         },
56880         
56881     getColumnRenderers : function(){
56882         var renderers = [];
56883         var cm = this.grid.colModel;
56884         var colCount = cm.getColumnCount();
56885         for(var i = 0; i < colCount; i++){
56886             renderers[i] = cm.getRenderer(i);
56887         }
56888         return renderers;
56889     },
56890     
56891     getColumnIds : function(){
56892         var ids = [];
56893         var cm = this.grid.colModel;
56894         var colCount = cm.getColumnCount();
56895         for(var i = 0; i < colCount; i++){
56896             ids[i] = cm.getColumnId(i);
56897         }
56898         return ids;
56899     },
56900     
56901     getDataIndexes : function(){
56902         if(!this.indexMap){
56903             this.indexMap = this.buildIndexMap();
56904         }
56905         return this.indexMap.colToData;
56906     },
56907     
56908     getColumnIndexByDataIndex : function(dataIndex){
56909         if(!this.indexMap){
56910             this.indexMap = this.buildIndexMap();
56911         }
56912         return this.indexMap.dataToCol[dataIndex];
56913     },
56914     
56915     /**
56916      * Set a css style for a column dynamically. 
56917      * @param {Number} colIndex The index of the column
56918      * @param {String} name The css property name
56919      * @param {String} value The css value
56920      */
56921     setCSSStyle : function(colIndex, name, value){
56922         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56923         Roo.util.CSS.updateRule(selector, name, value);
56924     },
56925     
56926     generateRules : function(cm){
56927         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56928         Roo.util.CSS.removeStyleSheet(rulesId);
56929         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56930             var cid = cm.getColumnId(i);
56931             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56932                          this.tdSelector, cid, " {\n}\n",
56933                          this.hdSelector, cid, " {\n}\n",
56934                          this.splitSelector, cid, " {\n}\n");
56935         }
56936         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56937     }
56938 });/*
56939  * Based on:
56940  * Ext JS Library 1.1.1
56941  * Copyright(c) 2006-2007, Ext JS, LLC.
56942  *
56943  * Originally Released Under LGPL - original licence link has changed is not relivant.
56944  *
56945  * Fork - LGPL
56946  * <script type="text/javascript">
56947  */
56948
56949 // private
56950 // This is a support class used internally by the Grid components
56951 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56952     this.grid = grid;
56953     this.view = grid.getView();
56954     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56955     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56956     if(hd2){
56957         this.setHandleElId(Roo.id(hd));
56958         this.setOuterHandleElId(Roo.id(hd2));
56959     }
56960     this.scroll = false;
56961 };
56962 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56963     maxDragWidth: 120,
56964     getDragData : function(e){
56965         var t = Roo.lib.Event.getTarget(e);
56966         var h = this.view.findHeaderCell(t);
56967         if(h){
56968             return {ddel: h.firstChild, header:h};
56969         }
56970         return false;
56971     },
56972
56973     onInitDrag : function(e){
56974         this.view.headersDisabled = true;
56975         var clone = this.dragData.ddel.cloneNode(true);
56976         clone.id = Roo.id();
56977         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56978         this.proxy.update(clone);
56979         return true;
56980     },
56981
56982     afterValidDrop : function(){
56983         var v = this.view;
56984         setTimeout(function(){
56985             v.headersDisabled = false;
56986         }, 50);
56987     },
56988
56989     afterInvalidDrop : function(){
56990         var v = this.view;
56991         setTimeout(function(){
56992             v.headersDisabled = false;
56993         }, 50);
56994     }
56995 });
56996 /*
56997  * Based on:
56998  * Ext JS Library 1.1.1
56999  * Copyright(c) 2006-2007, Ext JS, LLC.
57000  *
57001  * Originally Released Under LGPL - original licence link has changed is not relivant.
57002  *
57003  * Fork - LGPL
57004  * <script type="text/javascript">
57005  */
57006 // private
57007 // This is a support class used internally by the Grid components
57008 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57009     this.grid = grid;
57010     this.view = grid.getView();
57011     // split the proxies so they don't interfere with mouse events
57012     this.proxyTop = Roo.DomHelper.append(document.body, {
57013         cls:"col-move-top", html:"&#160;"
57014     }, true);
57015     this.proxyBottom = Roo.DomHelper.append(document.body, {
57016         cls:"col-move-bottom", html:"&#160;"
57017     }, true);
57018     this.proxyTop.hide = this.proxyBottom.hide = function(){
57019         this.setLeftTop(-100,-100);
57020         this.setStyle("visibility", "hidden");
57021     };
57022     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57023     // temporarily disabled
57024     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57025     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57026 };
57027 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57028     proxyOffsets : [-4, -9],
57029     fly: Roo.Element.fly,
57030
57031     getTargetFromEvent : function(e){
57032         var t = Roo.lib.Event.getTarget(e);
57033         var cindex = this.view.findCellIndex(t);
57034         if(cindex !== false){
57035             return this.view.getHeaderCell(cindex);
57036         }
57037         return null;
57038     },
57039
57040     nextVisible : function(h){
57041         var v = this.view, cm = this.grid.colModel;
57042         h = h.nextSibling;
57043         while(h){
57044             if(!cm.isHidden(v.getCellIndex(h))){
57045                 return h;
57046             }
57047             h = h.nextSibling;
57048         }
57049         return null;
57050     },
57051
57052     prevVisible : function(h){
57053         var v = this.view, cm = this.grid.colModel;
57054         h = h.prevSibling;
57055         while(h){
57056             if(!cm.isHidden(v.getCellIndex(h))){
57057                 return h;
57058             }
57059             h = h.prevSibling;
57060         }
57061         return null;
57062     },
57063
57064     positionIndicator : function(h, n, e){
57065         var x = Roo.lib.Event.getPageX(e);
57066         var r = Roo.lib.Dom.getRegion(n.firstChild);
57067         var px, pt, py = r.top + this.proxyOffsets[1];
57068         if((r.right - x) <= (r.right-r.left)/2){
57069             px = r.right+this.view.borderWidth;
57070             pt = "after";
57071         }else{
57072             px = r.left;
57073             pt = "before";
57074         }
57075         var oldIndex = this.view.getCellIndex(h);
57076         var newIndex = this.view.getCellIndex(n);
57077
57078         if(this.grid.colModel.isFixed(newIndex)){
57079             return false;
57080         }
57081
57082         var locked = this.grid.colModel.isLocked(newIndex);
57083
57084         if(pt == "after"){
57085             newIndex++;
57086         }
57087         if(oldIndex < newIndex){
57088             newIndex--;
57089         }
57090         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57091             return false;
57092         }
57093         px +=  this.proxyOffsets[0];
57094         this.proxyTop.setLeftTop(px, py);
57095         this.proxyTop.show();
57096         if(!this.bottomOffset){
57097             this.bottomOffset = this.view.mainHd.getHeight();
57098         }
57099         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57100         this.proxyBottom.show();
57101         return pt;
57102     },
57103
57104     onNodeEnter : function(n, dd, e, data){
57105         if(data.header != n){
57106             this.positionIndicator(data.header, n, e);
57107         }
57108     },
57109
57110     onNodeOver : function(n, dd, e, data){
57111         var result = false;
57112         if(data.header != n){
57113             result = this.positionIndicator(data.header, n, e);
57114         }
57115         if(!result){
57116             this.proxyTop.hide();
57117             this.proxyBottom.hide();
57118         }
57119         return result ? this.dropAllowed : this.dropNotAllowed;
57120     },
57121
57122     onNodeOut : function(n, dd, e, data){
57123         this.proxyTop.hide();
57124         this.proxyBottom.hide();
57125     },
57126
57127     onNodeDrop : function(n, dd, e, data){
57128         var h = data.header;
57129         if(h != n){
57130             var cm = this.grid.colModel;
57131             var x = Roo.lib.Event.getPageX(e);
57132             var r = Roo.lib.Dom.getRegion(n.firstChild);
57133             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57134             var oldIndex = this.view.getCellIndex(h);
57135             var newIndex = this.view.getCellIndex(n);
57136             var locked = cm.isLocked(newIndex);
57137             if(pt == "after"){
57138                 newIndex++;
57139             }
57140             if(oldIndex < newIndex){
57141                 newIndex--;
57142             }
57143             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57144                 return false;
57145             }
57146             cm.setLocked(oldIndex, locked, true);
57147             cm.moveColumn(oldIndex, newIndex);
57148             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57149             return true;
57150         }
57151         return false;
57152     }
57153 });
57154 /*
57155  * Based on:
57156  * Ext JS Library 1.1.1
57157  * Copyright(c) 2006-2007, Ext JS, LLC.
57158  *
57159  * Originally Released Under LGPL - original licence link has changed is not relivant.
57160  *
57161  * Fork - LGPL
57162  * <script type="text/javascript">
57163  */
57164   
57165 /**
57166  * @class Roo.grid.GridView
57167  * @extends Roo.util.Observable
57168  *
57169  * @constructor
57170  * @param {Object} config
57171  */
57172 Roo.grid.GridView = function(config){
57173     Roo.grid.GridView.superclass.constructor.call(this);
57174     this.el = null;
57175
57176     Roo.apply(this, config);
57177 };
57178
57179 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57180
57181     unselectable :  'unselectable="on"',
57182     unselectableCls :  'x-unselectable',
57183     
57184     
57185     rowClass : "x-grid-row",
57186
57187     cellClass : "x-grid-col",
57188
57189     tdClass : "x-grid-td",
57190
57191     hdClass : "x-grid-hd",
57192
57193     splitClass : "x-grid-split",
57194
57195     sortClasses : ["sort-asc", "sort-desc"],
57196
57197     enableMoveAnim : false,
57198
57199     hlColor: "C3DAF9",
57200
57201     dh : Roo.DomHelper,
57202
57203     fly : Roo.Element.fly,
57204
57205     css : Roo.util.CSS,
57206
57207     borderWidth: 1,
57208
57209     splitOffset: 3,
57210
57211     scrollIncrement : 22,
57212
57213     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57214
57215     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57216
57217     bind : function(ds, cm){
57218         if(this.ds){
57219             this.ds.un("load", this.onLoad, this);
57220             this.ds.un("datachanged", this.onDataChange, this);
57221             this.ds.un("add", this.onAdd, this);
57222             this.ds.un("remove", this.onRemove, this);
57223             this.ds.un("update", this.onUpdate, this);
57224             this.ds.un("clear", this.onClear, this);
57225         }
57226         if(ds){
57227             ds.on("load", this.onLoad, this);
57228             ds.on("datachanged", this.onDataChange, this);
57229             ds.on("add", this.onAdd, this);
57230             ds.on("remove", this.onRemove, this);
57231             ds.on("update", this.onUpdate, this);
57232             ds.on("clear", this.onClear, this);
57233         }
57234         this.ds = ds;
57235
57236         if(this.cm){
57237             this.cm.un("widthchange", this.onColWidthChange, this);
57238             this.cm.un("headerchange", this.onHeaderChange, this);
57239             this.cm.un("hiddenchange", this.onHiddenChange, this);
57240             this.cm.un("columnmoved", this.onColumnMove, this);
57241             this.cm.un("columnlockchange", this.onColumnLock, this);
57242         }
57243         if(cm){
57244             this.generateRules(cm);
57245             cm.on("widthchange", this.onColWidthChange, this);
57246             cm.on("headerchange", this.onHeaderChange, this);
57247             cm.on("hiddenchange", this.onHiddenChange, this);
57248             cm.on("columnmoved", this.onColumnMove, this);
57249             cm.on("columnlockchange", this.onColumnLock, this);
57250         }
57251         this.cm = cm;
57252     },
57253
57254     init: function(grid){
57255         Roo.grid.GridView.superclass.init.call(this, grid);
57256
57257         this.bind(grid.dataSource, grid.colModel);
57258
57259         grid.on("headerclick", this.handleHeaderClick, this);
57260
57261         if(grid.trackMouseOver){
57262             grid.on("mouseover", this.onRowOver, this);
57263             grid.on("mouseout", this.onRowOut, this);
57264         }
57265         grid.cancelTextSelection = function(){};
57266         this.gridId = grid.id;
57267
57268         var tpls = this.templates || {};
57269
57270         if(!tpls.master){
57271             tpls.master = new Roo.Template(
57272                '<div class="x-grid" hidefocus="true">',
57273                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57274                   '<div class="x-grid-topbar"></div>',
57275                   '<div class="x-grid-scroller"><div></div></div>',
57276                   '<div class="x-grid-locked">',
57277                       '<div class="x-grid-header">{lockedHeader}</div>',
57278                       '<div class="x-grid-body">{lockedBody}</div>',
57279                   "</div>",
57280                   '<div class="x-grid-viewport">',
57281                       '<div class="x-grid-header">{header}</div>',
57282                       '<div class="x-grid-body">{body}</div>',
57283                   "</div>",
57284                   '<div class="x-grid-bottombar"></div>',
57285                  
57286                   '<div class="x-grid-resize-proxy">&#160;</div>',
57287                "</div>"
57288             );
57289             tpls.master.disableformats = true;
57290         }
57291
57292         if(!tpls.header){
57293             tpls.header = new Roo.Template(
57294                '<table border="0" cellspacing="0" cellpadding="0">',
57295                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57296                "</table>{splits}"
57297             );
57298             tpls.header.disableformats = true;
57299         }
57300         tpls.header.compile();
57301
57302         if(!tpls.hcell){
57303             tpls.hcell = new Roo.Template(
57304                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57305                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57306                 "</div></td>"
57307              );
57308              tpls.hcell.disableFormats = true;
57309         }
57310         tpls.hcell.compile();
57311
57312         if(!tpls.hsplit){
57313             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57314                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57315             tpls.hsplit.disableFormats = true;
57316         }
57317         tpls.hsplit.compile();
57318
57319         if(!tpls.body){
57320             tpls.body = new Roo.Template(
57321                '<table border="0" cellspacing="0" cellpadding="0">',
57322                "<tbody>{rows}</tbody>",
57323                "</table>"
57324             );
57325             tpls.body.disableFormats = true;
57326         }
57327         tpls.body.compile();
57328
57329         if(!tpls.row){
57330             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57331             tpls.row.disableFormats = true;
57332         }
57333         tpls.row.compile();
57334
57335         if(!tpls.cell){
57336             tpls.cell = new Roo.Template(
57337                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57338                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57339                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57340                 "</td>"
57341             );
57342             tpls.cell.disableFormats = true;
57343         }
57344         tpls.cell.compile();
57345
57346         this.templates = tpls;
57347     },
57348
57349     // remap these for backwards compat
57350     onColWidthChange : function(){
57351         this.updateColumns.apply(this, arguments);
57352     },
57353     onHeaderChange : function(){
57354         this.updateHeaders.apply(this, arguments);
57355     }, 
57356     onHiddenChange : function(){
57357         this.handleHiddenChange.apply(this, arguments);
57358     },
57359     onColumnMove : function(){
57360         this.handleColumnMove.apply(this, arguments);
57361     },
57362     onColumnLock : function(){
57363         this.handleLockChange.apply(this, arguments);
57364     },
57365
57366     onDataChange : function(){
57367         this.refresh();
57368         this.updateHeaderSortState();
57369     },
57370
57371     onClear : function(){
57372         this.refresh();
57373     },
57374
57375     onUpdate : function(ds, record){
57376         this.refreshRow(record);
57377     },
57378
57379     refreshRow : function(record){
57380         var ds = this.ds, index;
57381         if(typeof record == 'number'){
57382             index = record;
57383             record = ds.getAt(index);
57384         }else{
57385             index = ds.indexOf(record);
57386         }
57387         this.insertRows(ds, index, index, true);
57388         this.onRemove(ds, record, index+1, true);
57389         this.syncRowHeights(index, index);
57390         this.layout();
57391         this.fireEvent("rowupdated", this, index, record);
57392     },
57393
57394     onAdd : function(ds, records, index){
57395         this.insertRows(ds, index, index + (records.length-1));
57396     },
57397
57398     onRemove : function(ds, record, index, isUpdate){
57399         if(isUpdate !== true){
57400             this.fireEvent("beforerowremoved", this, index, record);
57401         }
57402         var bt = this.getBodyTable(), lt = this.getLockedTable();
57403         if(bt.rows[index]){
57404             bt.firstChild.removeChild(bt.rows[index]);
57405         }
57406         if(lt.rows[index]){
57407             lt.firstChild.removeChild(lt.rows[index]);
57408         }
57409         if(isUpdate !== true){
57410             this.stripeRows(index);
57411             this.syncRowHeights(index, index);
57412             this.layout();
57413             this.fireEvent("rowremoved", this, index, record);
57414         }
57415     },
57416
57417     onLoad : function(){
57418         this.scrollToTop();
57419     },
57420
57421     /**
57422      * Scrolls the grid to the top
57423      */
57424     scrollToTop : function(){
57425         if(this.scroller){
57426             this.scroller.dom.scrollTop = 0;
57427             this.syncScroll();
57428         }
57429     },
57430
57431     /**
57432      * Gets a panel in the header of the grid that can be used for toolbars etc.
57433      * After modifying the contents of this panel a call to grid.autoSize() may be
57434      * required to register any changes in size.
57435      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57436      * @return Roo.Element
57437      */
57438     getHeaderPanel : function(doShow){
57439         if(doShow){
57440             this.headerPanel.show();
57441         }
57442         return this.headerPanel;
57443     },
57444
57445     /**
57446      * Gets a panel in the footer of the grid that can be used for toolbars etc.
57447      * After modifying the contents of this panel a call to grid.autoSize() may be
57448      * required to register any changes in size.
57449      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
57450      * @return Roo.Element
57451      */
57452     getFooterPanel : function(doShow){
57453         if(doShow){
57454             this.footerPanel.show();
57455         }
57456         return this.footerPanel;
57457     },
57458
57459     initElements : function(){
57460         var E = Roo.Element;
57461         var el = this.grid.getGridEl().dom.firstChild;
57462         var cs = el.childNodes;
57463
57464         this.el = new E(el);
57465         
57466          this.focusEl = new E(el.firstChild);
57467         this.focusEl.swallowEvent("click", true);
57468         
57469         this.headerPanel = new E(cs[1]);
57470         this.headerPanel.enableDisplayMode("block");
57471
57472         this.scroller = new E(cs[2]);
57473         this.scrollSizer = new E(this.scroller.dom.firstChild);
57474
57475         this.lockedWrap = new E(cs[3]);
57476         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
57477         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
57478
57479         this.mainWrap = new E(cs[4]);
57480         this.mainHd = new E(this.mainWrap.dom.firstChild);
57481         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
57482
57483         this.footerPanel = new E(cs[5]);
57484         this.footerPanel.enableDisplayMode("block");
57485
57486         this.resizeProxy = new E(cs[6]);
57487
57488         this.headerSelector = String.format(
57489            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
57490            this.lockedHd.id, this.mainHd.id
57491         );
57492
57493         this.splitterSelector = String.format(
57494            '#{0} div.x-grid-split, #{1} div.x-grid-split',
57495            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
57496         );
57497     },
57498     idToCssName : function(s)
57499     {
57500         return s.replace(/[^a-z0-9]+/ig, '-');
57501     },
57502
57503     getHeaderCell : function(index){
57504         return Roo.DomQuery.select(this.headerSelector)[index];
57505     },
57506
57507     getHeaderCellMeasure : function(index){
57508         return this.getHeaderCell(index).firstChild;
57509     },
57510
57511     getHeaderCellText : function(index){
57512         return this.getHeaderCell(index).firstChild.firstChild;
57513     },
57514
57515     getLockedTable : function(){
57516         return this.lockedBody.dom.firstChild;
57517     },
57518
57519     getBodyTable : function(){
57520         return this.mainBody.dom.firstChild;
57521     },
57522
57523     getLockedRow : function(index){
57524         return this.getLockedTable().rows[index];
57525     },
57526
57527     getRow : function(index){
57528         return this.getBodyTable().rows[index];
57529     },
57530
57531     getRowComposite : function(index){
57532         if(!this.rowEl){
57533             this.rowEl = new Roo.CompositeElementLite();
57534         }
57535         var els = [], lrow, mrow;
57536         if(lrow = this.getLockedRow(index)){
57537             els.push(lrow);
57538         }
57539         if(mrow = this.getRow(index)){
57540             els.push(mrow);
57541         }
57542         this.rowEl.elements = els;
57543         return this.rowEl;
57544     },
57545     /**
57546      * Gets the 'td' of the cell
57547      * 
57548      * @param {Integer} rowIndex row to select
57549      * @param {Integer} colIndex column to select
57550      * 
57551      * @return {Object} 
57552      */
57553     getCell : function(rowIndex, colIndex){
57554         var locked = this.cm.getLockedCount();
57555         var source;
57556         if(colIndex < locked){
57557             source = this.lockedBody.dom.firstChild;
57558         }else{
57559             source = this.mainBody.dom.firstChild;
57560             colIndex -= locked;
57561         }
57562         return source.rows[rowIndex].childNodes[colIndex];
57563     },
57564
57565     getCellText : function(rowIndex, colIndex){
57566         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
57567     },
57568
57569     getCellBox : function(cell){
57570         var b = this.fly(cell).getBox();
57571         if(Roo.isOpera){ // opera fails to report the Y
57572             b.y = cell.offsetTop + this.mainBody.getY();
57573         }
57574         return b;
57575     },
57576
57577     getCellIndex : function(cell){
57578         var id = String(cell.className).match(this.cellRE);
57579         if(id){
57580             return parseInt(id[1], 10);
57581         }
57582         return 0;
57583     },
57584
57585     findHeaderIndex : function(n){
57586         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57587         return r ? this.getCellIndex(r) : false;
57588     },
57589
57590     findHeaderCell : function(n){
57591         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57592         return r ? r : false;
57593     },
57594
57595     findRowIndex : function(n){
57596         if(!n){
57597             return false;
57598         }
57599         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
57600         return r ? r.rowIndex : false;
57601     },
57602
57603     findCellIndex : function(node){
57604         var stop = this.el.dom;
57605         while(node && node != stop){
57606             if(this.findRE.test(node.className)){
57607                 return this.getCellIndex(node);
57608             }
57609             node = node.parentNode;
57610         }
57611         return false;
57612     },
57613
57614     getColumnId : function(index){
57615         return this.cm.getColumnId(index);
57616     },
57617
57618     getSplitters : function()
57619     {
57620         if(this.splitterSelector){
57621            return Roo.DomQuery.select(this.splitterSelector);
57622         }else{
57623             return null;
57624       }
57625     },
57626
57627     getSplitter : function(index){
57628         return this.getSplitters()[index];
57629     },
57630
57631     onRowOver : function(e, t){
57632         var row;
57633         if((row = this.findRowIndex(t)) !== false){
57634             this.getRowComposite(row).addClass("x-grid-row-over");
57635         }
57636     },
57637
57638     onRowOut : function(e, t){
57639         var row;
57640         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
57641             this.getRowComposite(row).removeClass("x-grid-row-over");
57642         }
57643     },
57644
57645     renderHeaders : function(){
57646         var cm = this.cm;
57647         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
57648         var cb = [], lb = [], sb = [], lsb = [], p = {};
57649         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57650             p.cellId = "x-grid-hd-0-" + i;
57651             p.splitId = "x-grid-csplit-0-" + i;
57652             p.id = cm.getColumnId(i);
57653             p.value = cm.getColumnHeader(i) || "";
57654             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
57655             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
57656             if(!cm.isLocked(i)){
57657                 cb[cb.length] = ct.apply(p);
57658                 sb[sb.length] = st.apply(p);
57659             }else{
57660                 lb[lb.length] = ct.apply(p);
57661                 lsb[lsb.length] = st.apply(p);
57662             }
57663         }
57664         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
57665                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
57666     },
57667
57668     updateHeaders : function(){
57669         var html = this.renderHeaders();
57670         this.lockedHd.update(html[0]);
57671         this.mainHd.update(html[1]);
57672     },
57673
57674     /**
57675      * Focuses the specified row.
57676      * @param {Number} row The row index
57677      */
57678     focusRow : function(row)
57679     {
57680         //Roo.log('GridView.focusRow');
57681         var x = this.scroller.dom.scrollLeft;
57682         this.focusCell(row, 0, false);
57683         this.scroller.dom.scrollLeft = x;
57684     },
57685
57686     /**
57687      * Focuses the specified cell.
57688      * @param {Number} row The row index
57689      * @param {Number} col The column index
57690      * @param {Boolean} hscroll false to disable horizontal scrolling
57691      */
57692     focusCell : function(row, col, hscroll)
57693     {
57694         //Roo.log('GridView.focusCell');
57695         var el = this.ensureVisible(row, col, hscroll);
57696         this.focusEl.alignTo(el, "tl-tl");
57697         if(Roo.isGecko){
57698             this.focusEl.focus();
57699         }else{
57700             this.focusEl.focus.defer(1, this.focusEl);
57701         }
57702     },
57703
57704     /**
57705      * Scrolls the specified cell into view
57706      * @param {Number} row The row index
57707      * @param {Number} col The column index
57708      * @param {Boolean} hscroll false to disable horizontal scrolling
57709      */
57710     ensureVisible : function(row, col, hscroll)
57711     {
57712         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
57713         //return null; //disable for testing.
57714         if(typeof row != "number"){
57715             row = row.rowIndex;
57716         }
57717         if(row < 0 && row >= this.ds.getCount()){
57718             return  null;
57719         }
57720         col = (col !== undefined ? col : 0);
57721         var cm = this.grid.colModel;
57722         while(cm.isHidden(col)){
57723             col++;
57724         }
57725
57726         var el = this.getCell(row, col);
57727         if(!el){
57728             return null;
57729         }
57730         var c = this.scroller.dom;
57731
57732         var ctop = parseInt(el.offsetTop, 10);
57733         var cleft = parseInt(el.offsetLeft, 10);
57734         var cbot = ctop + el.offsetHeight;
57735         var cright = cleft + el.offsetWidth;
57736         
57737         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57738         var stop = parseInt(c.scrollTop, 10);
57739         var sleft = parseInt(c.scrollLeft, 10);
57740         var sbot = stop + ch;
57741         var sright = sleft + c.clientWidth;
57742         /*
57743         Roo.log('GridView.ensureVisible:' +
57744                 ' ctop:' + ctop +
57745                 ' c.clientHeight:' + c.clientHeight +
57746                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57747                 ' stop:' + stop +
57748                 ' cbot:' + cbot +
57749                 ' sbot:' + sbot +
57750                 ' ch:' + ch  
57751                 );
57752         */
57753         if(ctop < stop){
57754             c.scrollTop = ctop;
57755             //Roo.log("set scrolltop to ctop DISABLE?");
57756         }else if(cbot > sbot){
57757             //Roo.log("set scrolltop to cbot-ch");
57758             c.scrollTop = cbot-ch;
57759         }
57760         
57761         if(hscroll !== false){
57762             if(cleft < sleft){
57763                 c.scrollLeft = cleft;
57764             }else if(cright > sright){
57765                 c.scrollLeft = cright-c.clientWidth;
57766             }
57767         }
57768          
57769         return el;
57770     },
57771
57772     updateColumns : function(){
57773         this.grid.stopEditing();
57774         var cm = this.grid.colModel, colIds = this.getColumnIds();
57775         //var totalWidth = cm.getTotalWidth();
57776         var pos = 0;
57777         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57778             //if(cm.isHidden(i)) continue;
57779             var w = cm.getColumnWidth(i);
57780             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57781             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57782         }
57783         this.updateSplitters();
57784     },
57785
57786     generateRules : function(cm){
57787         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57788         Roo.util.CSS.removeStyleSheet(rulesId);
57789         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57790             var cid = cm.getColumnId(i);
57791             var align = '';
57792             if(cm.config[i].align){
57793                 align = 'text-align:'+cm.config[i].align+';';
57794             }
57795             var hidden = '';
57796             if(cm.isHidden(i)){
57797                 hidden = 'display:none;';
57798             }
57799             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57800             ruleBuf.push(
57801                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57802                     this.hdSelector, cid, " {\n", align, width, "}\n",
57803                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57804                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57805         }
57806         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57807     },
57808
57809     updateSplitters : function(){
57810         var cm = this.cm, s = this.getSplitters();
57811         if(s){ // splitters not created yet
57812             var pos = 0, locked = true;
57813             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57814                 if(cm.isHidden(i)) {
57815                     continue;
57816                 }
57817                 var w = cm.getColumnWidth(i); // make sure it's a number
57818                 if(!cm.isLocked(i) && locked){
57819                     pos = 0;
57820                     locked = false;
57821                 }
57822                 pos += w;
57823                 s[i].style.left = (pos-this.splitOffset) + "px";
57824             }
57825         }
57826     },
57827
57828     handleHiddenChange : function(colModel, colIndex, hidden){
57829         if(hidden){
57830             this.hideColumn(colIndex);
57831         }else{
57832             this.unhideColumn(colIndex);
57833         }
57834     },
57835
57836     hideColumn : function(colIndex){
57837         var cid = this.getColumnId(colIndex);
57838         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57839         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57840         if(Roo.isSafari){
57841             this.updateHeaders();
57842         }
57843         this.updateSplitters();
57844         this.layout();
57845     },
57846
57847     unhideColumn : function(colIndex){
57848         var cid = this.getColumnId(colIndex);
57849         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57850         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57851
57852         if(Roo.isSafari){
57853             this.updateHeaders();
57854         }
57855         this.updateSplitters();
57856         this.layout();
57857     },
57858
57859     insertRows : function(dm, firstRow, lastRow, isUpdate){
57860         if(firstRow == 0 && lastRow == dm.getCount()-1){
57861             this.refresh();
57862         }else{
57863             if(!isUpdate){
57864                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57865             }
57866             var s = this.getScrollState();
57867             var markup = this.renderRows(firstRow, lastRow);
57868             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57869             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57870             this.restoreScroll(s);
57871             if(!isUpdate){
57872                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57873                 this.syncRowHeights(firstRow, lastRow);
57874                 this.stripeRows(firstRow);
57875                 this.layout();
57876             }
57877         }
57878     },
57879
57880     bufferRows : function(markup, target, index){
57881         var before = null, trows = target.rows, tbody = target.tBodies[0];
57882         if(index < trows.length){
57883             before = trows[index];
57884         }
57885         var b = document.createElement("div");
57886         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57887         var rows = b.firstChild.rows;
57888         for(var i = 0, len = rows.length; i < len; i++){
57889             if(before){
57890                 tbody.insertBefore(rows[0], before);
57891             }else{
57892                 tbody.appendChild(rows[0]);
57893             }
57894         }
57895         b.innerHTML = "";
57896         b = null;
57897     },
57898
57899     deleteRows : function(dm, firstRow, lastRow){
57900         if(dm.getRowCount()<1){
57901             this.fireEvent("beforerefresh", this);
57902             this.mainBody.update("");
57903             this.lockedBody.update("");
57904             this.fireEvent("refresh", this);
57905         }else{
57906             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57907             var bt = this.getBodyTable();
57908             var tbody = bt.firstChild;
57909             var rows = bt.rows;
57910             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57911                 tbody.removeChild(rows[firstRow]);
57912             }
57913             this.stripeRows(firstRow);
57914             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57915         }
57916     },
57917
57918     updateRows : function(dataSource, firstRow, lastRow){
57919         var s = this.getScrollState();
57920         this.refresh();
57921         this.restoreScroll(s);
57922     },
57923
57924     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57925         if(!noRefresh){
57926            this.refresh();
57927         }
57928         this.updateHeaderSortState();
57929     },
57930
57931     getScrollState : function(){
57932         
57933         var sb = this.scroller.dom;
57934         return {left: sb.scrollLeft, top: sb.scrollTop};
57935     },
57936
57937     stripeRows : function(startRow){
57938         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57939             return;
57940         }
57941         startRow = startRow || 0;
57942         var rows = this.getBodyTable().rows;
57943         var lrows = this.getLockedTable().rows;
57944         var cls = ' x-grid-row-alt ';
57945         for(var i = startRow, len = rows.length; i < len; i++){
57946             var row = rows[i], lrow = lrows[i];
57947             var isAlt = ((i+1) % 2 == 0);
57948             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57949             if(isAlt == hasAlt){
57950                 continue;
57951             }
57952             if(isAlt){
57953                 row.className += " x-grid-row-alt";
57954             }else{
57955                 row.className = row.className.replace("x-grid-row-alt", "");
57956             }
57957             if(lrow){
57958                 lrow.className = row.className;
57959             }
57960         }
57961     },
57962
57963     restoreScroll : function(state){
57964         //Roo.log('GridView.restoreScroll');
57965         var sb = this.scroller.dom;
57966         sb.scrollLeft = state.left;
57967         sb.scrollTop = state.top;
57968         this.syncScroll();
57969     },
57970
57971     syncScroll : function(){
57972         //Roo.log('GridView.syncScroll');
57973         var sb = this.scroller.dom;
57974         var sh = this.mainHd.dom;
57975         var bs = this.mainBody.dom;
57976         var lv = this.lockedBody.dom;
57977         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57978         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57979     },
57980
57981     handleScroll : function(e){
57982         this.syncScroll();
57983         var sb = this.scroller.dom;
57984         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57985         e.stopEvent();
57986     },
57987
57988     handleWheel : function(e){
57989         var d = e.getWheelDelta();
57990         this.scroller.dom.scrollTop -= d*22;
57991         // set this here to prevent jumpy scrolling on large tables
57992         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57993         e.stopEvent();
57994     },
57995
57996     renderRows : function(startRow, endRow){
57997         // pull in all the crap needed to render rows
57998         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57999         var colCount = cm.getColumnCount();
58000
58001         if(ds.getCount() < 1){
58002             return ["", ""];
58003         }
58004
58005         // build a map for all the columns
58006         var cs = [];
58007         for(var i = 0; i < colCount; i++){
58008             var name = cm.getDataIndex(i);
58009             cs[i] = {
58010                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58011                 renderer : cm.getRenderer(i),
58012                 id : cm.getColumnId(i),
58013                 locked : cm.isLocked(i),
58014                 has_editor : cm.isCellEditable(i)
58015             };
58016         }
58017
58018         startRow = startRow || 0;
58019         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58020
58021         // records to render
58022         var rs = ds.getRange(startRow, endRow);
58023
58024         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58025     },
58026
58027     // As much as I hate to duplicate code, this was branched because FireFox really hates
58028     // [].join("") on strings. The performance difference was substantial enough to
58029     // branch this function
58030     doRender : Roo.isGecko ?
58031             function(cs, rs, ds, startRow, colCount, stripe){
58032                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58033                 // buffers
58034                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58035                 
58036                 var hasListener = this.grid.hasListener('rowclass');
58037                 var rowcfg = {};
58038                 for(var j = 0, len = rs.length; j < len; j++){
58039                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58040                     for(var i = 0; i < colCount; i++){
58041                         c = cs[i];
58042                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58043                         p.id = c.id;
58044                         p.css = p.attr = "";
58045                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58046                         if(p.value == undefined || p.value === "") {
58047                             p.value = "&#160;";
58048                         }
58049                         if(c.has_editor){
58050                             p.css += ' x-grid-editable-cell';
58051                         }
58052                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58053                             p.css +=  ' x-grid-dirty-cell';
58054                         }
58055                         var markup = ct.apply(p);
58056                         if(!c.locked){
58057                             cb+= markup;
58058                         }else{
58059                             lcb+= markup;
58060                         }
58061                     }
58062                     var alt = [];
58063                     if(stripe && ((rowIndex+1) % 2 == 0)){
58064                         alt.push("x-grid-row-alt")
58065                     }
58066                     if(r.dirty){
58067                         alt.push(  " x-grid-dirty-row");
58068                     }
58069                     rp.cells = lcb;
58070                     if(this.getRowClass){
58071                         alt.push(this.getRowClass(r, rowIndex));
58072                     }
58073                     if (hasListener) {
58074                         rowcfg = {
58075                              
58076                             record: r,
58077                             rowIndex : rowIndex,
58078                             rowClass : ''
58079                         };
58080                         this.grid.fireEvent('rowclass', this, rowcfg);
58081                         alt.push(rowcfg.rowClass);
58082                     }
58083                     rp.alt = alt.join(" ");
58084                     lbuf+= rt.apply(rp);
58085                     rp.cells = cb;
58086                     buf+=  rt.apply(rp);
58087                 }
58088                 return [lbuf, buf];
58089             } :
58090             function(cs, rs, ds, startRow, colCount, stripe){
58091                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58092                 // buffers
58093                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58094                 var hasListener = this.grid.hasListener('rowclass');
58095  
58096                 var rowcfg = {};
58097                 for(var j = 0, len = rs.length; j < len; j++){
58098                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58099                     for(var i = 0; i < colCount; i++){
58100                         c = cs[i];
58101                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58102                         p.id = c.id;
58103                         p.css = p.attr = "";
58104                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58105                         if(p.value == undefined || p.value === "") {
58106                             p.value = "&#160;";
58107                         }
58108                         //Roo.log(c);
58109                          if(c.has_editor){
58110                             p.css += ' x-grid-editable-cell';
58111                         }
58112                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58113                             p.css += ' x-grid-dirty-cell' 
58114                         }
58115                         
58116                         var markup = ct.apply(p);
58117                         if(!c.locked){
58118                             cb[cb.length] = markup;
58119                         }else{
58120                             lcb[lcb.length] = markup;
58121                         }
58122                     }
58123                     var alt = [];
58124                     if(stripe && ((rowIndex+1) % 2 == 0)){
58125                         alt.push( "x-grid-row-alt");
58126                     }
58127                     if(r.dirty){
58128                         alt.push(" x-grid-dirty-row");
58129                     }
58130                     rp.cells = lcb;
58131                     if(this.getRowClass){
58132                         alt.push( this.getRowClass(r, rowIndex));
58133                     }
58134                     if (hasListener) {
58135                         rowcfg = {
58136                              
58137                             record: r,
58138                             rowIndex : rowIndex,
58139                             rowClass : ''
58140                         };
58141                         this.grid.fireEvent('rowclass', this, rowcfg);
58142                         alt.push(rowcfg.rowClass);
58143                     }
58144                     
58145                     rp.alt = alt.join(" ");
58146                     rp.cells = lcb.join("");
58147                     lbuf[lbuf.length] = rt.apply(rp);
58148                     rp.cells = cb.join("");
58149                     buf[buf.length] =  rt.apply(rp);
58150                 }
58151                 return [lbuf.join(""), buf.join("")];
58152             },
58153
58154     renderBody : function(){
58155         var markup = this.renderRows();
58156         var bt = this.templates.body;
58157         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58158     },
58159
58160     /**
58161      * Refreshes the grid
58162      * @param {Boolean} headersToo
58163      */
58164     refresh : function(headersToo){
58165         this.fireEvent("beforerefresh", this);
58166         this.grid.stopEditing();
58167         var result = this.renderBody();
58168         this.lockedBody.update(result[0]);
58169         this.mainBody.update(result[1]);
58170         if(headersToo === true){
58171             this.updateHeaders();
58172             this.updateColumns();
58173             this.updateSplitters();
58174             this.updateHeaderSortState();
58175         }
58176         this.syncRowHeights();
58177         this.layout();
58178         this.fireEvent("refresh", this);
58179     },
58180
58181     handleColumnMove : function(cm, oldIndex, newIndex){
58182         this.indexMap = null;
58183         var s = this.getScrollState();
58184         this.refresh(true);
58185         this.restoreScroll(s);
58186         this.afterMove(newIndex);
58187     },
58188
58189     afterMove : function(colIndex){
58190         if(this.enableMoveAnim && Roo.enableFx){
58191             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58192         }
58193         // if multisort - fix sortOrder, and reload..
58194         if (this.grid.dataSource.multiSort) {
58195             // the we can call sort again..
58196             var dm = this.grid.dataSource;
58197             var cm = this.grid.colModel;
58198             var so = [];
58199             for(var i = 0; i < cm.config.length; i++ ) {
58200                 
58201                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58202                     continue; // dont' bother, it's not in sort list or being set.
58203                 }
58204                 
58205                 so.push(cm.config[i].dataIndex);
58206             };
58207             dm.sortOrder = so;
58208             dm.load(dm.lastOptions);
58209             
58210             
58211         }
58212         
58213     },
58214
58215     updateCell : function(dm, rowIndex, dataIndex){
58216         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58217         if(typeof colIndex == "undefined"){ // not present in grid
58218             return;
58219         }
58220         var cm = this.grid.colModel;
58221         var cell = this.getCell(rowIndex, colIndex);
58222         var cellText = this.getCellText(rowIndex, colIndex);
58223
58224         var p = {
58225             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58226             id : cm.getColumnId(colIndex),
58227             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58228         };
58229         var renderer = cm.getRenderer(colIndex);
58230         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58231         if(typeof val == "undefined" || val === "") {
58232             val = "&#160;";
58233         }
58234         cellText.innerHTML = val;
58235         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58236         this.syncRowHeights(rowIndex, rowIndex);
58237     },
58238
58239     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58240         var maxWidth = 0;
58241         if(this.grid.autoSizeHeaders){
58242             var h = this.getHeaderCellMeasure(colIndex);
58243             maxWidth = Math.max(maxWidth, h.scrollWidth);
58244         }
58245         var tb, index;
58246         if(this.cm.isLocked(colIndex)){
58247             tb = this.getLockedTable();
58248             index = colIndex;
58249         }else{
58250             tb = this.getBodyTable();
58251             index = colIndex - this.cm.getLockedCount();
58252         }
58253         if(tb && tb.rows){
58254             var rows = tb.rows;
58255             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58256             for(var i = 0; i < stopIndex; i++){
58257                 var cell = rows[i].childNodes[index].firstChild;
58258                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58259             }
58260         }
58261         return maxWidth + /*margin for error in IE*/ 5;
58262     },
58263     /**
58264      * Autofit a column to its content.
58265      * @param {Number} colIndex
58266      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58267      */
58268      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58269          if(this.cm.isHidden(colIndex)){
58270              return; // can't calc a hidden column
58271          }
58272         if(forceMinSize){
58273             var cid = this.cm.getColumnId(colIndex);
58274             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58275            if(this.grid.autoSizeHeaders){
58276                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58277            }
58278         }
58279         var newWidth = this.calcColumnWidth(colIndex);
58280         this.cm.setColumnWidth(colIndex,
58281             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58282         if(!suppressEvent){
58283             this.grid.fireEvent("columnresize", colIndex, newWidth);
58284         }
58285     },
58286
58287     /**
58288      * Autofits all columns to their content and then expands to fit any extra space in the grid
58289      */
58290      autoSizeColumns : function(){
58291         var cm = this.grid.colModel;
58292         var colCount = cm.getColumnCount();
58293         for(var i = 0; i < colCount; i++){
58294             this.autoSizeColumn(i, true, true);
58295         }
58296         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58297             this.fitColumns();
58298         }else{
58299             this.updateColumns();
58300             this.layout();
58301         }
58302     },
58303
58304     /**
58305      * Autofits all columns to the grid's width proportionate with their current size
58306      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58307      */
58308     fitColumns : function(reserveScrollSpace){
58309         var cm = this.grid.colModel;
58310         var colCount = cm.getColumnCount();
58311         var cols = [];
58312         var width = 0;
58313         var i, w;
58314         for (i = 0; i < colCount; i++){
58315             if(!cm.isHidden(i) && !cm.isFixed(i)){
58316                 w = cm.getColumnWidth(i);
58317                 cols.push(i);
58318                 cols.push(w);
58319                 width += w;
58320             }
58321         }
58322         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58323         if(reserveScrollSpace){
58324             avail -= 17;
58325         }
58326         var frac = (avail - cm.getTotalWidth())/width;
58327         while (cols.length){
58328             w = cols.pop();
58329             i = cols.pop();
58330             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58331         }
58332         this.updateColumns();
58333         this.layout();
58334     },
58335
58336     onRowSelect : function(rowIndex){
58337         var row = this.getRowComposite(rowIndex);
58338         row.addClass("x-grid-row-selected");
58339     },
58340
58341     onRowDeselect : function(rowIndex){
58342         var row = this.getRowComposite(rowIndex);
58343         row.removeClass("x-grid-row-selected");
58344     },
58345
58346     onCellSelect : function(row, col){
58347         var cell = this.getCell(row, col);
58348         if(cell){
58349             Roo.fly(cell).addClass("x-grid-cell-selected");
58350         }
58351     },
58352
58353     onCellDeselect : function(row, col){
58354         var cell = this.getCell(row, col);
58355         if(cell){
58356             Roo.fly(cell).removeClass("x-grid-cell-selected");
58357         }
58358     },
58359
58360     updateHeaderSortState : function(){
58361         
58362         // sort state can be single { field: xxx, direction : yyy}
58363         // or   { xxx=>ASC , yyy : DESC ..... }
58364         
58365         var mstate = {};
58366         if (!this.ds.multiSort) { 
58367             var state = this.ds.getSortState();
58368             if(!state){
58369                 return;
58370             }
58371             mstate[state.field] = state.direction;
58372             // FIXME... - this is not used here.. but might be elsewhere..
58373             this.sortState = state;
58374             
58375         } else {
58376             mstate = this.ds.sortToggle;
58377         }
58378         //remove existing sort classes..
58379         
58380         var sc = this.sortClasses;
58381         var hds = this.el.select(this.headerSelector).removeClass(sc);
58382         
58383         for(var f in mstate) {
58384         
58385             var sortColumn = this.cm.findColumnIndex(f);
58386             
58387             if(sortColumn != -1){
58388                 var sortDir = mstate[f];        
58389                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58390             }
58391         }
58392         
58393          
58394         
58395     },
58396
58397
58398     handleHeaderClick : function(g, index,e){
58399         
58400         Roo.log("header click");
58401         
58402         if (Roo.isTouch) {
58403             // touch events on header are handled by context
58404             this.handleHdCtx(g,index,e);
58405             return;
58406         }
58407         
58408         
58409         if(this.headersDisabled){
58410             return;
58411         }
58412         var dm = g.dataSource, cm = g.colModel;
58413         if(!cm.isSortable(index)){
58414             return;
58415         }
58416         g.stopEditing();
58417         
58418         if (dm.multiSort) {
58419             // update the sortOrder
58420             var so = [];
58421             for(var i = 0; i < cm.config.length; i++ ) {
58422                 
58423                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58424                     continue; // dont' bother, it's not in sort list or being set.
58425                 }
58426                 
58427                 so.push(cm.config[i].dataIndex);
58428             };
58429             dm.sortOrder = so;
58430         }
58431         
58432         
58433         dm.sort(cm.getDataIndex(index));
58434     },
58435
58436
58437     destroy : function(){
58438         if(this.colMenu){
58439             this.colMenu.removeAll();
58440             Roo.menu.MenuMgr.unregister(this.colMenu);
58441             this.colMenu.getEl().remove();
58442             delete this.colMenu;
58443         }
58444         if(this.hmenu){
58445             this.hmenu.removeAll();
58446             Roo.menu.MenuMgr.unregister(this.hmenu);
58447             this.hmenu.getEl().remove();
58448             delete this.hmenu;
58449         }
58450         if(this.grid.enableColumnMove){
58451             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58452             if(dds){
58453                 for(var dd in dds){
58454                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
58455                         var elid = dds[dd].dragElId;
58456                         dds[dd].unreg();
58457                         Roo.get(elid).remove();
58458                     } else if(dds[dd].config.isTarget){
58459                         dds[dd].proxyTop.remove();
58460                         dds[dd].proxyBottom.remove();
58461                         dds[dd].unreg();
58462                     }
58463                     if(Roo.dd.DDM.locationCache[dd]){
58464                         delete Roo.dd.DDM.locationCache[dd];
58465                     }
58466                 }
58467                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58468             }
58469         }
58470         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
58471         this.bind(null, null);
58472         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
58473     },
58474
58475     handleLockChange : function(){
58476         this.refresh(true);
58477     },
58478
58479     onDenyColumnLock : function(){
58480
58481     },
58482
58483     onDenyColumnHide : function(){
58484
58485     },
58486
58487     handleHdMenuClick : function(item){
58488         var index = this.hdCtxIndex;
58489         var cm = this.cm, ds = this.ds;
58490         switch(item.id){
58491             case "asc":
58492                 ds.sort(cm.getDataIndex(index), "ASC");
58493                 break;
58494             case "desc":
58495                 ds.sort(cm.getDataIndex(index), "DESC");
58496                 break;
58497             case "lock":
58498                 var lc = cm.getLockedCount();
58499                 if(cm.getColumnCount(true) <= lc+1){
58500                     this.onDenyColumnLock();
58501                     return;
58502                 }
58503                 if(lc != index){
58504                     cm.setLocked(index, true, true);
58505                     cm.moveColumn(index, lc);
58506                     this.grid.fireEvent("columnmove", index, lc);
58507                 }else{
58508                     cm.setLocked(index, true);
58509                 }
58510             break;
58511             case "unlock":
58512                 var lc = cm.getLockedCount();
58513                 if((lc-1) != index){
58514                     cm.setLocked(index, false, true);
58515                     cm.moveColumn(index, lc-1);
58516                     this.grid.fireEvent("columnmove", index, lc-1);
58517                 }else{
58518                     cm.setLocked(index, false);
58519                 }
58520             break;
58521             case 'wider': // used to expand cols on touch..
58522             case 'narrow':
58523                 var cw = cm.getColumnWidth(index);
58524                 cw += (item.id == 'wider' ? 1 : -1) * 50;
58525                 cw = Math.max(0, cw);
58526                 cw = Math.min(cw,4000);
58527                 cm.setColumnWidth(index, cw);
58528                 break;
58529                 
58530             default:
58531                 index = cm.getIndexById(item.id.substr(4));
58532                 if(index != -1){
58533                     if(item.checked && cm.getColumnCount(true) <= 1){
58534                         this.onDenyColumnHide();
58535                         return false;
58536                     }
58537                     cm.setHidden(index, item.checked);
58538                 }
58539         }
58540         return true;
58541     },
58542
58543     beforeColMenuShow : function(){
58544         var cm = this.cm,  colCount = cm.getColumnCount();
58545         this.colMenu.removeAll();
58546         for(var i = 0; i < colCount; i++){
58547             this.colMenu.add(new Roo.menu.CheckItem({
58548                 id: "col-"+cm.getColumnId(i),
58549                 text: cm.getColumnHeader(i),
58550                 checked: !cm.isHidden(i),
58551                 hideOnClick:false
58552             }));
58553         }
58554     },
58555
58556     handleHdCtx : function(g, index, e){
58557         e.stopEvent();
58558         var hd = this.getHeaderCell(index);
58559         this.hdCtxIndex = index;
58560         var ms = this.hmenu.items, cm = this.cm;
58561         ms.get("asc").setDisabled(!cm.isSortable(index));
58562         ms.get("desc").setDisabled(!cm.isSortable(index));
58563         if(this.grid.enableColLock !== false){
58564             ms.get("lock").setDisabled(cm.isLocked(index));
58565             ms.get("unlock").setDisabled(!cm.isLocked(index));
58566         }
58567         this.hmenu.show(hd, "tl-bl");
58568     },
58569
58570     handleHdOver : function(e){
58571         var hd = this.findHeaderCell(e.getTarget());
58572         if(hd && !this.headersDisabled){
58573             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
58574                this.fly(hd).addClass("x-grid-hd-over");
58575             }
58576         }
58577     },
58578
58579     handleHdOut : function(e){
58580         var hd = this.findHeaderCell(e.getTarget());
58581         if(hd){
58582             this.fly(hd).removeClass("x-grid-hd-over");
58583         }
58584     },
58585
58586     handleSplitDblClick : function(e, t){
58587         var i = this.getCellIndex(t);
58588         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
58589             this.autoSizeColumn(i, true);
58590             this.layout();
58591         }
58592     },
58593
58594     render : function(){
58595
58596         var cm = this.cm;
58597         var colCount = cm.getColumnCount();
58598
58599         if(this.grid.monitorWindowResize === true){
58600             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58601         }
58602         var header = this.renderHeaders();
58603         var body = this.templates.body.apply({rows:""});
58604         var html = this.templates.master.apply({
58605             lockedBody: body,
58606             body: body,
58607             lockedHeader: header[0],
58608             header: header[1]
58609         });
58610
58611         //this.updateColumns();
58612
58613         this.grid.getGridEl().dom.innerHTML = html;
58614
58615         this.initElements();
58616         
58617         // a kludge to fix the random scolling effect in webkit
58618         this.el.on("scroll", function() {
58619             this.el.dom.scrollTop=0; // hopefully not recursive..
58620         },this);
58621
58622         this.scroller.on("scroll", this.handleScroll, this);
58623         this.lockedBody.on("mousewheel", this.handleWheel, this);
58624         this.mainBody.on("mousewheel", this.handleWheel, this);
58625
58626         this.mainHd.on("mouseover", this.handleHdOver, this);
58627         this.mainHd.on("mouseout", this.handleHdOut, this);
58628         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
58629                 {delegate: "."+this.splitClass});
58630
58631         this.lockedHd.on("mouseover", this.handleHdOver, this);
58632         this.lockedHd.on("mouseout", this.handleHdOut, this);
58633         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
58634                 {delegate: "."+this.splitClass});
58635
58636         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
58637             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58638         }
58639
58640         this.updateSplitters();
58641
58642         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
58643             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58644             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58645         }
58646
58647         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
58648             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
58649             this.hmenu.add(
58650                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
58651                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
58652             );
58653             if(this.grid.enableColLock !== false){
58654                 this.hmenu.add('-',
58655                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
58656                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
58657                 );
58658             }
58659             if (Roo.isTouch) {
58660                  this.hmenu.add('-',
58661                     {id:"wider", text: this.columnsWiderText},
58662                     {id:"narrow", text: this.columnsNarrowText }
58663                 );
58664                 
58665                  
58666             }
58667             
58668             if(this.grid.enableColumnHide !== false){
58669
58670                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
58671                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
58672                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
58673
58674                 this.hmenu.add('-',
58675                     {id:"columns", text: this.columnsText, menu: this.colMenu}
58676                 );
58677             }
58678             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
58679
58680             this.grid.on("headercontextmenu", this.handleHdCtx, this);
58681         }
58682
58683         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
58684             this.dd = new Roo.grid.GridDragZone(this.grid, {
58685                 ddGroup : this.grid.ddGroup || 'GridDD'
58686             });
58687             
58688         }
58689
58690         /*
58691         for(var i = 0; i < colCount; i++){
58692             if(cm.isHidden(i)){
58693                 this.hideColumn(i);
58694             }
58695             if(cm.config[i].align){
58696                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
58697                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
58698             }
58699         }*/
58700         
58701         this.updateHeaderSortState();
58702
58703         this.beforeInitialResize();
58704         this.layout(true);
58705
58706         // two part rendering gives faster view to the user
58707         this.renderPhase2.defer(1, this);
58708     },
58709
58710     renderPhase2 : function(){
58711         // render the rows now
58712         this.refresh();
58713         if(this.grid.autoSizeColumns){
58714             this.autoSizeColumns();
58715         }
58716     },
58717
58718     beforeInitialResize : function(){
58719
58720     },
58721
58722     onColumnSplitterMoved : function(i, w){
58723         this.userResized = true;
58724         var cm = this.grid.colModel;
58725         cm.setColumnWidth(i, w, true);
58726         var cid = cm.getColumnId(i);
58727         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58728         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58729         this.updateSplitters();
58730         this.layout();
58731         this.grid.fireEvent("columnresize", i, w);
58732     },
58733
58734     syncRowHeights : function(startIndex, endIndex){
58735         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58736             startIndex = startIndex || 0;
58737             var mrows = this.getBodyTable().rows;
58738             var lrows = this.getLockedTable().rows;
58739             var len = mrows.length-1;
58740             endIndex = Math.min(endIndex || len, len);
58741             for(var i = startIndex; i <= endIndex; i++){
58742                 var m = mrows[i], l = lrows[i];
58743                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58744                 m.style.height = l.style.height = h + "px";
58745             }
58746         }
58747     },
58748
58749     layout : function(initialRender, is2ndPass)
58750     {
58751         var g = this.grid;
58752         var auto = g.autoHeight;
58753         var scrollOffset = 16;
58754         var c = g.getGridEl(), cm = this.cm,
58755                 expandCol = g.autoExpandColumn,
58756                 gv = this;
58757         //c.beginMeasure();
58758
58759         if(!c.dom.offsetWidth){ // display:none?
58760             if(initialRender){
58761                 this.lockedWrap.show();
58762                 this.mainWrap.show();
58763             }
58764             return;
58765         }
58766
58767         var hasLock = this.cm.isLocked(0);
58768
58769         var tbh = this.headerPanel.getHeight();
58770         var bbh = this.footerPanel.getHeight();
58771
58772         if(auto){
58773             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58774             var newHeight = ch + c.getBorderWidth("tb");
58775             if(g.maxHeight){
58776                 newHeight = Math.min(g.maxHeight, newHeight);
58777             }
58778             c.setHeight(newHeight);
58779         }
58780
58781         if(g.autoWidth){
58782             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58783         }
58784
58785         var s = this.scroller;
58786
58787         var csize = c.getSize(true);
58788
58789         this.el.setSize(csize.width, csize.height);
58790
58791         this.headerPanel.setWidth(csize.width);
58792         this.footerPanel.setWidth(csize.width);
58793
58794         var hdHeight = this.mainHd.getHeight();
58795         var vw = csize.width;
58796         var vh = csize.height - (tbh + bbh);
58797
58798         s.setSize(vw, vh);
58799
58800         var bt = this.getBodyTable();
58801         
58802         if(cm.getLockedCount() == cm.config.length){
58803             bt = this.getLockedTable();
58804         }
58805         
58806         var ltWidth = hasLock ?
58807                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58808
58809         var scrollHeight = bt.offsetHeight;
58810         var scrollWidth = ltWidth + bt.offsetWidth;
58811         var vscroll = false, hscroll = false;
58812
58813         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58814
58815         var lw = this.lockedWrap, mw = this.mainWrap;
58816         var lb = this.lockedBody, mb = this.mainBody;
58817
58818         setTimeout(function(){
58819             var t = s.dom.offsetTop;
58820             var w = s.dom.clientWidth,
58821                 h = s.dom.clientHeight;
58822
58823             lw.setTop(t);
58824             lw.setSize(ltWidth, h);
58825
58826             mw.setLeftTop(ltWidth, t);
58827             mw.setSize(w-ltWidth, h);
58828
58829             lb.setHeight(h-hdHeight);
58830             mb.setHeight(h-hdHeight);
58831
58832             if(is2ndPass !== true && !gv.userResized && expandCol){
58833                 // high speed resize without full column calculation
58834                 
58835                 var ci = cm.getIndexById(expandCol);
58836                 if (ci < 0) {
58837                     ci = cm.findColumnIndex(expandCol);
58838                 }
58839                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58840                 var expandId = cm.getColumnId(ci);
58841                 var  tw = cm.getTotalWidth(false);
58842                 var currentWidth = cm.getColumnWidth(ci);
58843                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58844                 if(currentWidth != cw){
58845                     cm.setColumnWidth(ci, cw, true);
58846                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58847                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58848                     gv.updateSplitters();
58849                     gv.layout(false, true);
58850                 }
58851             }
58852
58853             if(initialRender){
58854                 lw.show();
58855                 mw.show();
58856             }
58857             //c.endMeasure();
58858         }, 10);
58859     },
58860
58861     onWindowResize : function(){
58862         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58863             return;
58864         }
58865         this.layout();
58866     },
58867
58868     appendFooter : function(parentEl){
58869         return null;
58870     },
58871
58872     sortAscText : "Sort Ascending",
58873     sortDescText : "Sort Descending",
58874     lockText : "Lock Column",
58875     unlockText : "Unlock Column",
58876     columnsText : "Columns",
58877  
58878     columnsWiderText : "Wider",
58879     columnsNarrowText : "Thinner"
58880 });
58881
58882
58883 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58884     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58885     this.proxy.el.addClass('x-grid3-col-dd');
58886 };
58887
58888 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58889     handleMouseDown : function(e){
58890
58891     },
58892
58893     callHandleMouseDown : function(e){
58894         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58895     }
58896 });
58897 /*
58898  * Based on:
58899  * Ext JS Library 1.1.1
58900  * Copyright(c) 2006-2007, Ext JS, LLC.
58901  *
58902  * Originally Released Under LGPL - original licence link has changed is not relivant.
58903  *
58904  * Fork - LGPL
58905  * <script type="text/javascript">
58906  */
58907  /**
58908  * @extends Roo.dd.DDProxy
58909  * @class Roo.grid.SplitDragZone
58910  * Support for Column Header resizing
58911  * @constructor
58912  * @param {Object} config
58913  */
58914 // private
58915 // This is a support class used internally by the Grid components
58916 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58917     this.grid = grid;
58918     this.view = grid.getView();
58919     this.proxy = this.view.resizeProxy;
58920     Roo.grid.SplitDragZone.superclass.constructor.call(
58921         this,
58922         hd, // ID
58923         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58924         {  // CONFIG
58925             dragElId : Roo.id(this.proxy.dom),
58926             resizeFrame:false
58927         }
58928     );
58929     
58930     this.setHandleElId(Roo.id(hd));
58931     if (hd2 !== false) {
58932         this.setOuterHandleElId(Roo.id(hd2));
58933     }
58934     
58935     this.scroll = false;
58936 };
58937 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58938     fly: Roo.Element.fly,
58939
58940     b4StartDrag : function(x, y){
58941         this.view.headersDisabled = true;
58942         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58943                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58944         );
58945         this.proxy.setHeight(h);
58946         
58947         // for old system colWidth really stored the actual width?
58948         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58949         // which in reality did not work.. - it worked only for fixed sizes
58950         // for resizable we need to use actual sizes.
58951         var w = this.cm.getColumnWidth(this.cellIndex);
58952         if (!this.view.mainWrap) {
58953             // bootstrap.
58954             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58955         }
58956         
58957         
58958         
58959         // this was w-this.grid.minColumnWidth;
58960         // doesnt really make sense? - w = thie curren width or the rendered one?
58961         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58962         this.resetConstraints();
58963         this.setXConstraint(minw, 1000);
58964         this.setYConstraint(0, 0);
58965         this.minX = x - minw;
58966         this.maxX = x + 1000;
58967         this.startPos = x;
58968         if (!this.view.mainWrap) { // this is Bootstrap code..
58969             this.getDragEl().style.display='block';
58970         }
58971         
58972         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58973     },
58974
58975
58976     handleMouseDown : function(e){
58977         ev = Roo.EventObject.setEvent(e);
58978         var t = this.fly(ev.getTarget());
58979         if(t.hasClass("x-grid-split")){
58980             this.cellIndex = this.view.getCellIndex(t.dom);
58981             this.split = t.dom;
58982             this.cm = this.grid.colModel;
58983             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58984                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58985             }
58986         }
58987     },
58988
58989     endDrag : function(e){
58990         this.view.headersDisabled = false;
58991         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58992         var diff = endX - this.startPos;
58993         // 
58994         var w = this.cm.getColumnWidth(this.cellIndex);
58995         if (!this.view.mainWrap) {
58996             w = 0;
58997         }
58998         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58999     },
59000
59001     autoOffset : function(){
59002         this.setDelta(0,0);
59003     }
59004 });/*
59005  * Based on:
59006  * Ext JS Library 1.1.1
59007  * Copyright(c) 2006-2007, Ext JS, LLC.
59008  *
59009  * Originally Released Under LGPL - original licence link has changed is not relivant.
59010  *
59011  * Fork - LGPL
59012  * <script type="text/javascript">
59013  */
59014  
59015 // private
59016 // This is a support class used internally by the Grid components
59017 Roo.grid.GridDragZone = function(grid, config){
59018     this.view = grid.getView();
59019     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59020     if(this.view.lockedBody){
59021         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59022         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59023     }
59024     this.scroll = false;
59025     this.grid = grid;
59026     this.ddel = document.createElement('div');
59027     this.ddel.className = 'x-grid-dd-wrap';
59028 };
59029
59030 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59031     ddGroup : "GridDD",
59032
59033     getDragData : function(e){
59034         var t = Roo.lib.Event.getTarget(e);
59035         var rowIndex = this.view.findRowIndex(t);
59036         var sm = this.grid.selModel;
59037             
59038         //Roo.log(rowIndex);
59039         
59040         if (sm.getSelectedCell) {
59041             // cell selection..
59042             if (!sm.getSelectedCell()) {
59043                 return false;
59044             }
59045             if (rowIndex != sm.getSelectedCell()[0]) {
59046                 return false;
59047             }
59048         
59049         }
59050         if (sm.getSelections && sm.getSelections().length < 1) {
59051             return false;
59052         }
59053         
59054         
59055         // before it used to all dragging of unseleted... - now we dont do that.
59056         if(rowIndex !== false){
59057             
59058             // if editorgrid.. 
59059             
59060             
59061             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59062                
59063             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59064               //  
59065             //}
59066             if (e.hasModifier()){
59067                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59068             }
59069             
59070             Roo.log("getDragData");
59071             
59072             return {
59073                 grid: this.grid,
59074                 ddel: this.ddel,
59075                 rowIndex: rowIndex,
59076                 selections: sm.getSelections ? sm.getSelections() : (
59077                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59078             };
59079         }
59080         return false;
59081     },
59082     
59083     
59084     onInitDrag : function(e){
59085         var data = this.dragData;
59086         this.ddel.innerHTML = this.grid.getDragDropText();
59087         this.proxy.update(this.ddel);
59088         // fire start drag?
59089     },
59090
59091     afterRepair : function(){
59092         this.dragging = false;
59093     },
59094
59095     getRepairXY : function(e, data){
59096         return false;
59097     },
59098
59099     onEndDrag : function(data, e){
59100         // fire end drag?
59101     },
59102
59103     onValidDrop : function(dd, e, id){
59104         // fire drag drop?
59105         this.hideProxy();
59106     },
59107
59108     beforeInvalidDrop : function(e, id){
59109
59110     }
59111 });/*
59112  * Based on:
59113  * Ext JS Library 1.1.1
59114  * Copyright(c) 2006-2007, Ext JS, LLC.
59115  *
59116  * Originally Released Under LGPL - original licence link has changed is not relivant.
59117  *
59118  * Fork - LGPL
59119  * <script type="text/javascript">
59120  */
59121  
59122
59123 /**
59124  * @class Roo.grid.ColumnModel
59125  * @extends Roo.util.Observable
59126  * This is the default implementation of a ColumnModel used by the Grid. It defines
59127  * the columns in the grid.
59128  * <br>Usage:<br>
59129  <pre><code>
59130  var colModel = new Roo.grid.ColumnModel([
59131         {header: "Ticker", width: 60, sortable: true, locked: true},
59132         {header: "Company Name", width: 150, sortable: true},
59133         {header: "Market Cap.", width: 100, sortable: true},
59134         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59135         {header: "Employees", width: 100, sortable: true, resizable: false}
59136  ]);
59137  </code></pre>
59138  * <p>
59139  
59140  * The config options listed for this class are options which may appear in each
59141  * individual column definition.
59142  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59143  * @constructor
59144  * @param {Object} config An Array of column config objects. See this class's
59145  * config objects for details.
59146 */
59147 Roo.grid.ColumnModel = function(config){
59148         /**
59149      * The config passed into the constructor
59150      */
59151     this.config = []; //config;
59152     this.lookup = {};
59153
59154     // if no id, create one
59155     // if the column does not have a dataIndex mapping,
59156     // map it to the order it is in the config
59157     for(var i = 0, len = config.length; i < len; i++){
59158         this.addColumn(config[i]);
59159         
59160     }
59161
59162     /**
59163      * The width of columns which have no width specified (defaults to 100)
59164      * @type Number
59165      */
59166     this.defaultWidth = 100;
59167
59168     /**
59169      * Default sortable of columns which have no sortable specified (defaults to false)
59170      * @type Boolean
59171      */
59172     this.defaultSortable = false;
59173
59174     this.addEvents({
59175         /**
59176              * @event widthchange
59177              * Fires when the width of a column changes.
59178              * @param {ColumnModel} this
59179              * @param {Number} columnIndex The column index
59180              * @param {Number} newWidth The new width
59181              */
59182             "widthchange": true,
59183         /**
59184              * @event headerchange
59185              * Fires when the text of a header changes.
59186              * @param {ColumnModel} this
59187              * @param {Number} columnIndex The column index
59188              * @param {Number} newText The new header text
59189              */
59190             "headerchange": true,
59191         /**
59192              * @event hiddenchange
59193              * Fires when a column is hidden or "unhidden".
59194              * @param {ColumnModel} this
59195              * @param {Number} columnIndex The column index
59196              * @param {Boolean} hidden true if hidden, false otherwise
59197              */
59198             "hiddenchange": true,
59199             /**
59200          * @event columnmoved
59201          * Fires when a column is moved.
59202          * @param {ColumnModel} this
59203          * @param {Number} oldIndex
59204          * @param {Number} newIndex
59205          */
59206         "columnmoved" : true,
59207         /**
59208          * @event columlockchange
59209          * Fires when a column's locked state is changed
59210          * @param {ColumnModel} this
59211          * @param {Number} colIndex
59212          * @param {Boolean} locked true if locked
59213          */
59214         "columnlockchange" : true
59215     });
59216     Roo.grid.ColumnModel.superclass.constructor.call(this);
59217 };
59218 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59219     /**
59220      * @cfg {String} header The header text to display in the Grid view.
59221      */
59222         /**
59223      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59224      */
59225         /**
59226      * @cfg {String} smHeader Header at Bootsrap Small width
59227      */
59228         /**
59229      * @cfg {String} mdHeader Header at Bootsrap Medium width
59230      */
59231         /**
59232      * @cfg {String} lgHeader Header at Bootsrap Large width
59233      */
59234         /**
59235      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59236      */
59237     /**
59238      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59239      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59240      * specified, the column's index is used as an index into the Record's data Array.
59241      */
59242     /**
59243      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59244      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59245      */
59246     /**
59247      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59248      * Defaults to the value of the {@link #defaultSortable} property.
59249      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59250      */
59251     /**
59252      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59253      */
59254     /**
59255      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59256      */
59257     /**
59258      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59259      */
59260     /**
59261      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59262      */
59263     /**
59264      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59265      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59266      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59267      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59268      */
59269        /**
59270      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59271      */
59272     /**
59273      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59274      */
59275     /**
59276      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59277      */
59278     /**
59279      * @cfg {String} cursor (Optional)
59280      */
59281     /**
59282      * @cfg {String} tooltip (Optional)
59283      */
59284     /**
59285      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59286      */
59287     /**
59288      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59289      */
59290     /**
59291      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59292      */
59293     /**
59294      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59295      */
59296         /**
59297      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59298      */
59299     /**
59300      * Returns the id of the column at the specified index.
59301      * @param {Number} index The column index
59302      * @return {String} the id
59303      */
59304     getColumnId : function(index){
59305         return this.config[index].id;
59306     },
59307
59308     /**
59309      * Returns the column for a specified id.
59310      * @param {String} id The column id
59311      * @return {Object} the column
59312      */
59313     getColumnById : function(id){
59314         return this.lookup[id];
59315     },
59316
59317     
59318     /**
59319      * Returns the column Object for a specified dataIndex.
59320      * @param {String} dataIndex The column dataIndex
59321      * @return {Object|Boolean} the column or false if not found
59322      */
59323     getColumnByDataIndex: function(dataIndex){
59324         var index = this.findColumnIndex(dataIndex);
59325         return index > -1 ? this.config[index] : false;
59326     },
59327     
59328     /**
59329      * Returns the index for a specified column id.
59330      * @param {String} id The column id
59331      * @return {Number} the index, or -1 if not found
59332      */
59333     getIndexById : function(id){
59334         for(var i = 0, len = this.config.length; i < len; i++){
59335             if(this.config[i].id == id){
59336                 return i;
59337             }
59338         }
59339         return -1;
59340     },
59341     
59342     /**
59343      * Returns the index for a specified column dataIndex.
59344      * @param {String} dataIndex The column dataIndex
59345      * @return {Number} the index, or -1 if not found
59346      */
59347     
59348     findColumnIndex : function(dataIndex){
59349         for(var i = 0, len = this.config.length; i < len; i++){
59350             if(this.config[i].dataIndex == dataIndex){
59351                 return i;
59352             }
59353         }
59354         return -1;
59355     },
59356     
59357     
59358     moveColumn : function(oldIndex, newIndex){
59359         var c = this.config[oldIndex];
59360         this.config.splice(oldIndex, 1);
59361         this.config.splice(newIndex, 0, c);
59362         this.dataMap = null;
59363         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59364     },
59365
59366     isLocked : function(colIndex){
59367         return this.config[colIndex].locked === true;
59368     },
59369
59370     setLocked : function(colIndex, value, suppressEvent){
59371         if(this.isLocked(colIndex) == value){
59372             return;
59373         }
59374         this.config[colIndex].locked = value;
59375         if(!suppressEvent){
59376             this.fireEvent("columnlockchange", this, colIndex, value);
59377         }
59378     },
59379
59380     getTotalLockedWidth : function(){
59381         var totalWidth = 0;
59382         for(var i = 0; i < this.config.length; i++){
59383             if(this.isLocked(i) && !this.isHidden(i)){
59384                 this.totalWidth += this.getColumnWidth(i);
59385             }
59386         }
59387         return totalWidth;
59388     },
59389
59390     getLockedCount : function(){
59391         for(var i = 0, len = this.config.length; i < len; i++){
59392             if(!this.isLocked(i)){
59393                 return i;
59394             }
59395         }
59396         
59397         return this.config.length;
59398     },
59399
59400     /**
59401      * Returns the number of columns.
59402      * @return {Number}
59403      */
59404     getColumnCount : function(visibleOnly){
59405         if(visibleOnly === true){
59406             var c = 0;
59407             for(var i = 0, len = this.config.length; i < len; i++){
59408                 if(!this.isHidden(i)){
59409                     c++;
59410                 }
59411             }
59412             return c;
59413         }
59414         return this.config.length;
59415     },
59416
59417     /**
59418      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59419      * @param {Function} fn
59420      * @param {Object} scope (optional)
59421      * @return {Array} result
59422      */
59423     getColumnsBy : function(fn, scope){
59424         var r = [];
59425         for(var i = 0, len = this.config.length; i < len; i++){
59426             var c = this.config[i];
59427             if(fn.call(scope||this, c, i) === true){
59428                 r[r.length] = c;
59429             }
59430         }
59431         return r;
59432     },
59433
59434     /**
59435      * Returns true if the specified column is sortable.
59436      * @param {Number} col The column index
59437      * @return {Boolean}
59438      */
59439     isSortable : function(col){
59440         if(typeof this.config[col].sortable == "undefined"){
59441             return this.defaultSortable;
59442         }
59443         return this.config[col].sortable;
59444     },
59445
59446     /**
59447      * Returns the rendering (formatting) function defined for the column.
59448      * @param {Number} col The column index.
59449      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
59450      */
59451     getRenderer : function(col){
59452         if(!this.config[col].renderer){
59453             return Roo.grid.ColumnModel.defaultRenderer;
59454         }
59455         return this.config[col].renderer;
59456     },
59457
59458     /**
59459      * Sets the rendering (formatting) function for a column.
59460      * @param {Number} col The column index
59461      * @param {Function} fn The function to use to process the cell's raw data
59462      * to return HTML markup for the grid view. The render function is called with
59463      * the following parameters:<ul>
59464      * <li>Data value.</li>
59465      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
59466      * <li>css A CSS style string to apply to the table cell.</li>
59467      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
59468      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
59469      * <li>Row index</li>
59470      * <li>Column index</li>
59471      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
59472      */
59473     setRenderer : function(col, fn){
59474         this.config[col].renderer = fn;
59475     },
59476
59477     /**
59478      * Returns the width for the specified column.
59479      * @param {Number} col The column index
59480      * @param (optional) {String} gridSize bootstrap width size.
59481      * @return {Number}
59482      */
59483     getColumnWidth : function(col, gridSize)
59484         {
59485                 var cfg = this.config[col];
59486                 
59487                 if (typeof(gridSize) == 'undefined') {
59488                         return cfg.width * 1 || this.defaultWidth;
59489                 }
59490                 if (gridSize === false) { // if we set it..
59491                         return cfg.width || false;
59492                 }
59493                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
59494                 
59495                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
59496                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
59497                                 continue;
59498                         }
59499                         return cfg[ sizes[i] ];
59500                 }
59501                 return 1;
59502                 
59503     },
59504
59505     /**
59506      * Sets the width for a column.
59507      * @param {Number} col The column index
59508      * @param {Number} width The new width
59509      */
59510     setColumnWidth : function(col, width, suppressEvent){
59511         this.config[col].width = width;
59512         this.totalWidth = null;
59513         if(!suppressEvent){
59514              this.fireEvent("widthchange", this, col, width);
59515         }
59516     },
59517
59518     /**
59519      * Returns the total width of all columns.
59520      * @param {Boolean} includeHidden True to include hidden column widths
59521      * @return {Number}
59522      */
59523     getTotalWidth : function(includeHidden){
59524         if(!this.totalWidth){
59525             this.totalWidth = 0;
59526             for(var i = 0, len = this.config.length; i < len; i++){
59527                 if(includeHidden || !this.isHidden(i)){
59528                     this.totalWidth += this.getColumnWidth(i);
59529                 }
59530             }
59531         }
59532         return this.totalWidth;
59533     },
59534
59535     /**
59536      * Returns the header for the specified column.
59537      * @param {Number} col The column index
59538      * @return {String}
59539      */
59540     getColumnHeader : function(col){
59541         return this.config[col].header;
59542     },
59543
59544     /**
59545      * Sets the header for a column.
59546      * @param {Number} col The column index
59547      * @param {String} header The new header
59548      */
59549     setColumnHeader : function(col, header){
59550         this.config[col].header = header;
59551         this.fireEvent("headerchange", this, col, header);
59552     },
59553
59554     /**
59555      * Returns the tooltip for the specified column.
59556      * @param {Number} col The column index
59557      * @return {String}
59558      */
59559     getColumnTooltip : function(col){
59560             return this.config[col].tooltip;
59561     },
59562     /**
59563      * Sets the tooltip for a column.
59564      * @param {Number} col The column index
59565      * @param {String} tooltip The new tooltip
59566      */
59567     setColumnTooltip : function(col, tooltip){
59568             this.config[col].tooltip = tooltip;
59569     },
59570
59571     /**
59572      * Returns the dataIndex for the specified column.
59573      * @param {Number} col The column index
59574      * @return {Number}
59575      */
59576     getDataIndex : function(col){
59577         return this.config[col].dataIndex;
59578     },
59579
59580     /**
59581      * Sets the dataIndex for a column.
59582      * @param {Number} col The column index
59583      * @param {Number} dataIndex The new dataIndex
59584      */
59585     setDataIndex : function(col, dataIndex){
59586         this.config[col].dataIndex = dataIndex;
59587     },
59588
59589     
59590     
59591     /**
59592      * Returns true if the cell is editable.
59593      * @param {Number} colIndex The column index
59594      * @param {Number} rowIndex The row index - this is nto actually used..?
59595      * @return {Boolean}
59596      */
59597     isCellEditable : function(colIndex, rowIndex){
59598         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
59599     },
59600
59601     /**
59602      * Returns the editor defined for the cell/column.
59603      * return false or null to disable editing.
59604      * @param {Number} colIndex The column index
59605      * @param {Number} rowIndex The row index
59606      * @return {Object}
59607      */
59608     getCellEditor : function(colIndex, rowIndex){
59609         return this.config[colIndex].editor;
59610     },
59611
59612     /**
59613      * Sets if a column is editable.
59614      * @param {Number} col The column index
59615      * @param {Boolean} editable True if the column is editable
59616      */
59617     setEditable : function(col, editable){
59618         this.config[col].editable = editable;
59619     },
59620
59621
59622     /**
59623      * Returns true if the column is hidden.
59624      * @param {Number} colIndex The column index
59625      * @return {Boolean}
59626      */
59627     isHidden : function(colIndex){
59628         return this.config[colIndex].hidden;
59629     },
59630
59631
59632     /**
59633      * Returns true if the column width cannot be changed
59634      */
59635     isFixed : function(colIndex){
59636         return this.config[colIndex].fixed;
59637     },
59638
59639     /**
59640      * Returns true if the column can be resized
59641      * @return {Boolean}
59642      */
59643     isResizable : function(colIndex){
59644         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
59645     },
59646     /**
59647      * Sets if a column is hidden.
59648      * @param {Number} colIndex The column index
59649      * @param {Boolean} hidden True if the column is hidden
59650      */
59651     setHidden : function(colIndex, hidden){
59652         this.config[colIndex].hidden = hidden;
59653         this.totalWidth = null;
59654         this.fireEvent("hiddenchange", this, colIndex, hidden);
59655     },
59656
59657     /**
59658      * Sets the editor for a column.
59659      * @param {Number} col The column index
59660      * @param {Object} editor The editor object
59661      */
59662     setEditor : function(col, editor){
59663         this.config[col].editor = editor;
59664     },
59665     /**
59666      * Add a column (experimental...) - defaults to adding to the end..
59667      * @param {Object} config 
59668     */
59669     addColumn : function(c)
59670     {
59671     
59672         var i = this.config.length;
59673         this.config[i] = c;
59674         
59675         if(typeof c.dataIndex == "undefined"){
59676             c.dataIndex = i;
59677         }
59678         if(typeof c.renderer == "string"){
59679             c.renderer = Roo.util.Format[c.renderer];
59680         }
59681         if(typeof c.id == "undefined"){
59682             c.id = Roo.id();
59683         }
59684         if(c.editor && c.editor.xtype){
59685             c.editor  = Roo.factory(c.editor, Roo.grid);
59686         }
59687         if(c.editor && c.editor.isFormField){
59688             c.editor = new Roo.grid.GridEditor(c.editor);
59689         }
59690         this.lookup[c.id] = c;
59691     }
59692     
59693 });
59694
59695 Roo.grid.ColumnModel.defaultRenderer = function(value)
59696 {
59697     if(typeof value == "object") {
59698         return value;
59699     }
59700         if(typeof value == "string" && value.length < 1){
59701             return "&#160;";
59702         }
59703     
59704         return String.format("{0}", value);
59705 };
59706
59707 // Alias for backwards compatibility
59708 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
59709 /*
59710  * Based on:
59711  * Ext JS Library 1.1.1
59712  * Copyright(c) 2006-2007, Ext JS, LLC.
59713  *
59714  * Originally Released Under LGPL - original licence link has changed is not relivant.
59715  *
59716  * Fork - LGPL
59717  * <script type="text/javascript">
59718  */
59719
59720 /**
59721  * @class Roo.grid.AbstractSelectionModel
59722  * @extends Roo.util.Observable
59723  * @abstract
59724  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59725  * implemented by descendant classes.  This class should not be directly instantiated.
59726  * @constructor
59727  */
59728 Roo.grid.AbstractSelectionModel = function(){
59729     this.locked = false;
59730     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59731 };
59732
59733 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59734     /** @ignore Called by the grid automatically. Do not call directly. */
59735     init : function(grid){
59736         this.grid = grid;
59737         this.initEvents();
59738     },
59739
59740     /**
59741      * Locks the selections.
59742      */
59743     lock : function(){
59744         this.locked = true;
59745     },
59746
59747     /**
59748      * Unlocks the selections.
59749      */
59750     unlock : function(){
59751         this.locked = false;
59752     },
59753
59754     /**
59755      * Returns true if the selections are locked.
59756      * @return {Boolean}
59757      */
59758     isLocked : function(){
59759         return this.locked;
59760     }
59761 });/*
59762  * Based on:
59763  * Ext JS Library 1.1.1
59764  * Copyright(c) 2006-2007, Ext JS, LLC.
59765  *
59766  * Originally Released Under LGPL - original licence link has changed is not relivant.
59767  *
59768  * Fork - LGPL
59769  * <script type="text/javascript">
59770  */
59771 /**
59772  * @extends Roo.grid.AbstractSelectionModel
59773  * @class Roo.grid.RowSelectionModel
59774  * The default SelectionModel used by {@link Roo.grid.Grid}.
59775  * It supports multiple selections and keyboard selection/navigation. 
59776  * @constructor
59777  * @param {Object} config
59778  */
59779 Roo.grid.RowSelectionModel = function(config){
59780     Roo.apply(this, config);
59781     this.selections = new Roo.util.MixedCollection(false, function(o){
59782         return o.id;
59783     });
59784
59785     this.last = false;
59786     this.lastActive = false;
59787
59788     this.addEvents({
59789         /**
59790         * @event selectionchange
59791         * Fires when the selection changes
59792         * @param {SelectionModel} this
59793         */
59794        "selectionchange" : true,
59795        /**
59796         * @event afterselectionchange
59797         * Fires after the selection changes (eg. by key press or clicking)
59798         * @param {SelectionModel} this
59799         */
59800        "afterselectionchange" : true,
59801        /**
59802         * @event beforerowselect
59803         * Fires when a row is selected being selected, return false to cancel.
59804         * @param {SelectionModel} this
59805         * @param {Number} rowIndex The selected index
59806         * @param {Boolean} keepExisting False if other selections will be cleared
59807         */
59808        "beforerowselect" : true,
59809        /**
59810         * @event rowselect
59811         * Fires when a row is selected.
59812         * @param {SelectionModel} this
59813         * @param {Number} rowIndex The selected index
59814         * @param {Roo.data.Record} r The record
59815         */
59816        "rowselect" : true,
59817        /**
59818         * @event rowdeselect
59819         * Fires when a row is deselected.
59820         * @param {SelectionModel} this
59821         * @param {Number} rowIndex The selected index
59822         */
59823         "rowdeselect" : true
59824     });
59825     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59826     this.locked = false;
59827 };
59828
59829 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59830     /**
59831      * @cfg {Boolean} singleSelect
59832      * True to allow selection of only one row at a time (defaults to false)
59833      */
59834     singleSelect : false,
59835
59836     // private
59837     initEvents : function(){
59838
59839         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59840             this.grid.on("mousedown", this.handleMouseDown, this);
59841         }else{ // allow click to work like normal
59842             this.grid.on("rowclick", this.handleDragableRowClick, this);
59843         }
59844         // bootstrap does not have a view..
59845         var view = this.grid.view ? this.grid.view : this.grid;
59846         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59847             "up" : function(e){
59848                 if(!e.shiftKey){
59849                     this.selectPrevious(e.shiftKey);
59850                 }else if(this.last !== false && this.lastActive !== false){
59851                     var last = this.last;
59852                     this.selectRange(this.last,  this.lastActive-1);
59853                     view.focusRow(this.lastActive);
59854                     if(last !== false){
59855                         this.last = last;
59856                     }
59857                 }else{
59858                     this.selectFirstRow();
59859                 }
59860                 this.fireEvent("afterselectionchange", this);
59861             },
59862             "down" : function(e){
59863                 if(!e.shiftKey){
59864                     this.selectNext(e.shiftKey);
59865                 }else if(this.last !== false && this.lastActive !== false){
59866                     var last = this.last;
59867                     this.selectRange(this.last,  this.lastActive+1);
59868                     view.focusRow(this.lastActive);
59869                     if(last !== false){
59870                         this.last = last;
59871                     }
59872                 }else{
59873                     this.selectFirstRow();
59874                 }
59875                 this.fireEvent("afterselectionchange", this);
59876             },
59877             scope: this
59878         });
59879
59880          
59881         view.on("refresh", this.onRefresh, this);
59882         view.on("rowupdated", this.onRowUpdated, this);
59883         view.on("rowremoved", this.onRemove, this);
59884     },
59885
59886     // private
59887     onRefresh : function(){
59888         var ds = this.grid.ds, i, v = this.grid.view;
59889         var s = this.selections;
59890         s.each(function(r){
59891             if((i = ds.indexOfId(r.id)) != -1){
59892                 v.onRowSelect(i);
59893                 s.add(ds.getAt(i)); // updating the selection relate data
59894             }else{
59895                 s.remove(r);
59896             }
59897         });
59898     },
59899
59900     // private
59901     onRemove : function(v, index, r){
59902         this.selections.remove(r);
59903     },
59904
59905     // private
59906     onRowUpdated : function(v, index, r){
59907         if(this.isSelected(r)){
59908             v.onRowSelect(index);
59909         }
59910     },
59911
59912     /**
59913      * Select records.
59914      * @param {Array} records The records to select
59915      * @param {Boolean} keepExisting (optional) True to keep existing selections
59916      */
59917     selectRecords : function(records, keepExisting){
59918         if(!keepExisting){
59919             this.clearSelections();
59920         }
59921         var ds = this.grid.ds;
59922         for(var i = 0, len = records.length; i < len; i++){
59923             this.selectRow(ds.indexOf(records[i]), true);
59924         }
59925     },
59926
59927     /**
59928      * Gets the number of selected rows.
59929      * @return {Number}
59930      */
59931     getCount : function(){
59932         return this.selections.length;
59933     },
59934
59935     /**
59936      * Selects the first row in the grid.
59937      */
59938     selectFirstRow : function(){
59939         this.selectRow(0);
59940     },
59941
59942     /**
59943      * Select the last row.
59944      * @param {Boolean} keepExisting (optional) True to keep existing selections
59945      */
59946     selectLastRow : function(keepExisting){
59947         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59948     },
59949
59950     /**
59951      * Selects the row immediately following the last selected row.
59952      * @param {Boolean} keepExisting (optional) True to keep existing selections
59953      */
59954     selectNext : function(keepExisting){
59955         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59956             this.selectRow(this.last+1, keepExisting);
59957             var view = this.grid.view ? this.grid.view : this.grid;
59958             view.focusRow(this.last);
59959         }
59960     },
59961
59962     /**
59963      * Selects the row that precedes the last selected row.
59964      * @param {Boolean} keepExisting (optional) True to keep existing selections
59965      */
59966     selectPrevious : function(keepExisting){
59967         if(this.last){
59968             this.selectRow(this.last-1, keepExisting);
59969             var view = this.grid.view ? this.grid.view : this.grid;
59970             view.focusRow(this.last);
59971         }
59972     },
59973
59974     /**
59975      * Returns the selected records
59976      * @return {Array} Array of selected records
59977      */
59978     getSelections : function(){
59979         return [].concat(this.selections.items);
59980     },
59981
59982     /**
59983      * Returns the first selected record.
59984      * @return {Record}
59985      */
59986     getSelected : function(){
59987         return this.selections.itemAt(0);
59988     },
59989
59990
59991     /**
59992      * Clears all selections.
59993      */
59994     clearSelections : function(fast){
59995         if(this.locked) {
59996             return;
59997         }
59998         if(fast !== true){
59999             var ds = this.grid.ds;
60000             var s = this.selections;
60001             s.each(function(r){
60002                 this.deselectRow(ds.indexOfId(r.id));
60003             }, this);
60004             s.clear();
60005         }else{
60006             this.selections.clear();
60007         }
60008         this.last = false;
60009     },
60010
60011
60012     /**
60013      * Selects all rows.
60014      */
60015     selectAll : function(){
60016         if(this.locked) {
60017             return;
60018         }
60019         this.selections.clear();
60020         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60021             this.selectRow(i, true);
60022         }
60023     },
60024
60025     /**
60026      * Returns True if there is a selection.
60027      * @return {Boolean}
60028      */
60029     hasSelection : function(){
60030         return this.selections.length > 0;
60031     },
60032
60033     /**
60034      * Returns True if the specified row is selected.
60035      * @param {Number/Record} record The record or index of the record to check
60036      * @return {Boolean}
60037      */
60038     isSelected : function(index){
60039         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60040         return (r && this.selections.key(r.id) ? true : false);
60041     },
60042
60043     /**
60044      * Returns True if the specified record id is selected.
60045      * @param {String} id The id of record to check
60046      * @return {Boolean}
60047      */
60048     isIdSelected : function(id){
60049         return (this.selections.key(id) ? true : false);
60050     },
60051
60052     // private
60053     handleMouseDown : function(e, t)
60054     {
60055         var view = this.grid.view ? this.grid.view : this.grid;
60056         var rowIndex;
60057         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60058             return;
60059         };
60060         if(e.shiftKey && this.last !== false){
60061             var last = this.last;
60062             this.selectRange(last, rowIndex, e.ctrlKey);
60063             this.last = last; // reset the last
60064             view.focusRow(rowIndex);
60065         }else{
60066             var isSelected = this.isSelected(rowIndex);
60067             if(e.button !== 0 && isSelected){
60068                 view.focusRow(rowIndex);
60069             }else if(e.ctrlKey && isSelected){
60070                 this.deselectRow(rowIndex);
60071             }else if(!isSelected){
60072                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60073                 view.focusRow(rowIndex);
60074             }
60075         }
60076         this.fireEvent("afterselectionchange", this);
60077     },
60078     // private
60079     handleDragableRowClick :  function(grid, rowIndex, e) 
60080     {
60081         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60082             this.selectRow(rowIndex, false);
60083             var view = this.grid.view ? this.grid.view : this.grid;
60084             view.focusRow(rowIndex);
60085              this.fireEvent("afterselectionchange", this);
60086         }
60087     },
60088     
60089     /**
60090      * Selects multiple rows.
60091      * @param {Array} rows Array of the indexes of the row to select
60092      * @param {Boolean} keepExisting (optional) True to keep existing selections
60093      */
60094     selectRows : function(rows, keepExisting){
60095         if(!keepExisting){
60096             this.clearSelections();
60097         }
60098         for(var i = 0, len = rows.length; i < len; i++){
60099             this.selectRow(rows[i], true);
60100         }
60101     },
60102
60103     /**
60104      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60105      * @param {Number} startRow The index of the first row in the range
60106      * @param {Number} endRow The index of the last row in the range
60107      * @param {Boolean} keepExisting (optional) True to retain existing selections
60108      */
60109     selectRange : function(startRow, endRow, keepExisting){
60110         if(this.locked) {
60111             return;
60112         }
60113         if(!keepExisting){
60114             this.clearSelections();
60115         }
60116         if(startRow <= endRow){
60117             for(var i = startRow; i <= endRow; i++){
60118                 this.selectRow(i, true);
60119             }
60120         }else{
60121             for(var i = startRow; i >= endRow; i--){
60122                 this.selectRow(i, true);
60123             }
60124         }
60125     },
60126
60127     /**
60128      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60129      * @param {Number} startRow The index of the first row in the range
60130      * @param {Number} endRow The index of the last row in the range
60131      */
60132     deselectRange : function(startRow, endRow, preventViewNotify){
60133         if(this.locked) {
60134             return;
60135         }
60136         for(var i = startRow; i <= endRow; i++){
60137             this.deselectRow(i, preventViewNotify);
60138         }
60139     },
60140
60141     /**
60142      * Selects a row.
60143      * @param {Number} row The index of the row to select
60144      * @param {Boolean} keepExisting (optional) True to keep existing selections
60145      */
60146     selectRow : function(index, keepExisting, preventViewNotify){
60147         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60148             return;
60149         }
60150         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60151             if(!keepExisting || this.singleSelect){
60152                 this.clearSelections();
60153             }
60154             var r = this.grid.ds.getAt(index);
60155             this.selections.add(r);
60156             this.last = this.lastActive = index;
60157             if(!preventViewNotify){
60158                 var view = this.grid.view ? this.grid.view : this.grid;
60159                 view.onRowSelect(index);
60160             }
60161             this.fireEvent("rowselect", this, index, r);
60162             this.fireEvent("selectionchange", this);
60163         }
60164     },
60165
60166     /**
60167      * Deselects a row.
60168      * @param {Number} row The index of the row to deselect
60169      */
60170     deselectRow : function(index, preventViewNotify){
60171         if(this.locked) {
60172             return;
60173         }
60174         if(this.last == index){
60175             this.last = false;
60176         }
60177         if(this.lastActive == index){
60178             this.lastActive = false;
60179         }
60180         var r = this.grid.ds.getAt(index);
60181         this.selections.remove(r);
60182         if(!preventViewNotify){
60183             var view = this.grid.view ? this.grid.view : this.grid;
60184             view.onRowDeselect(index);
60185         }
60186         this.fireEvent("rowdeselect", this, index);
60187         this.fireEvent("selectionchange", this);
60188     },
60189
60190     // private
60191     restoreLast : function(){
60192         if(this._last){
60193             this.last = this._last;
60194         }
60195     },
60196
60197     // private
60198     acceptsNav : function(row, col, cm){
60199         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60200     },
60201
60202     // private
60203     onEditorKey : function(field, e){
60204         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60205         if(k == e.TAB){
60206             e.stopEvent();
60207             ed.completeEdit();
60208             if(e.shiftKey){
60209                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60210             }else{
60211                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60212             }
60213         }else if(k == e.ENTER && !e.ctrlKey){
60214             e.stopEvent();
60215             ed.completeEdit();
60216             if(e.shiftKey){
60217                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60218             }else{
60219                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60220             }
60221         }else if(k == e.ESC){
60222             ed.cancelEdit();
60223         }
60224         if(newCell){
60225             g.startEditing(newCell[0], newCell[1]);
60226         }
60227     }
60228 });/*
60229  * Based on:
60230  * Ext JS Library 1.1.1
60231  * Copyright(c) 2006-2007, Ext JS, LLC.
60232  *
60233  * Originally Released Under LGPL - original licence link has changed is not relivant.
60234  *
60235  * Fork - LGPL
60236  * <script type="text/javascript">
60237  */
60238 /**
60239  * @class Roo.grid.CellSelectionModel
60240  * @extends Roo.grid.AbstractSelectionModel
60241  * This class provides the basic implementation for cell selection in a grid.
60242  * @constructor
60243  * @param {Object} config The object containing the configuration of this model.
60244  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60245  */
60246 Roo.grid.CellSelectionModel = function(config){
60247     Roo.apply(this, config);
60248
60249     this.selection = null;
60250
60251     this.addEvents({
60252         /**
60253              * @event beforerowselect
60254              * Fires before a cell is selected.
60255              * @param {SelectionModel} this
60256              * @param {Number} rowIndex The selected row index
60257              * @param {Number} colIndex The selected cell index
60258              */
60259             "beforecellselect" : true,
60260         /**
60261              * @event cellselect
60262              * Fires when a cell is selected.
60263              * @param {SelectionModel} this
60264              * @param {Number} rowIndex The selected row index
60265              * @param {Number} colIndex The selected cell index
60266              */
60267             "cellselect" : true,
60268         /**
60269              * @event selectionchange
60270              * Fires when the active selection changes.
60271              * @param {SelectionModel} this
60272              * @param {Object} selection null for no selection or an object (o) with two properties
60273                 <ul>
60274                 <li>o.record: the record object for the row the selection is in</li>
60275                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60276                 </ul>
60277              */
60278             "selectionchange" : true,
60279         /**
60280              * @event tabend
60281              * Fires when the tab (or enter) was pressed on the last editable cell
60282              * You can use this to trigger add new row.
60283              * @param {SelectionModel} this
60284              */
60285             "tabend" : true,
60286          /**
60287              * @event beforeeditnext
60288              * Fires before the next editable sell is made active
60289              * You can use this to skip to another cell or fire the tabend
60290              *    if you set cell to false
60291              * @param {Object} eventdata object : { cell : [ row, col ] } 
60292              */
60293             "beforeeditnext" : true
60294     });
60295     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60296 };
60297
60298 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60299     
60300     enter_is_tab: false,
60301
60302     /** @ignore */
60303     initEvents : function(){
60304         this.grid.on("mousedown", this.handleMouseDown, this);
60305         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60306         var view = this.grid.view;
60307         view.on("refresh", this.onViewChange, this);
60308         view.on("rowupdated", this.onRowUpdated, this);
60309         view.on("beforerowremoved", this.clearSelections, this);
60310         view.on("beforerowsinserted", this.clearSelections, this);
60311         if(this.grid.isEditor){
60312             this.grid.on("beforeedit", this.beforeEdit,  this);
60313         }
60314     },
60315
60316         //private
60317     beforeEdit : function(e){
60318         this.select(e.row, e.column, false, true, e.record);
60319     },
60320
60321         //private
60322     onRowUpdated : function(v, index, r){
60323         if(this.selection && this.selection.record == r){
60324             v.onCellSelect(index, this.selection.cell[1]);
60325         }
60326     },
60327
60328         //private
60329     onViewChange : function(){
60330         this.clearSelections(true);
60331     },
60332
60333         /**
60334          * Returns the currently selected cell,.
60335          * @return {Array} The selected cell (row, column) or null if none selected.
60336          */
60337     getSelectedCell : function(){
60338         return this.selection ? this.selection.cell : null;
60339     },
60340
60341     /**
60342      * Clears all selections.
60343      * @param {Boolean} true to prevent the gridview from being notified about the change.
60344      */
60345     clearSelections : function(preventNotify){
60346         var s = this.selection;
60347         if(s){
60348             if(preventNotify !== true){
60349                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60350             }
60351             this.selection = null;
60352             this.fireEvent("selectionchange", this, null);
60353         }
60354     },
60355
60356     /**
60357      * Returns true if there is a selection.
60358      * @return {Boolean}
60359      */
60360     hasSelection : function(){
60361         return this.selection ? true : false;
60362     },
60363
60364     /** @ignore */
60365     handleMouseDown : function(e, t){
60366         var v = this.grid.getView();
60367         if(this.isLocked()){
60368             return;
60369         };
60370         var row = v.findRowIndex(t);
60371         var cell = v.findCellIndex(t);
60372         if(row !== false && cell !== false){
60373             this.select(row, cell);
60374         }
60375     },
60376
60377     /**
60378      * Selects a cell.
60379      * @param {Number} rowIndex
60380      * @param {Number} collIndex
60381      */
60382     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60383         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60384             this.clearSelections();
60385             r = r || this.grid.dataSource.getAt(rowIndex);
60386             this.selection = {
60387                 record : r,
60388                 cell : [rowIndex, colIndex]
60389             };
60390             if(!preventViewNotify){
60391                 var v = this.grid.getView();
60392                 v.onCellSelect(rowIndex, colIndex);
60393                 if(preventFocus !== true){
60394                     v.focusCell(rowIndex, colIndex);
60395                 }
60396             }
60397             this.fireEvent("cellselect", this, rowIndex, colIndex);
60398             this.fireEvent("selectionchange", this, this.selection);
60399         }
60400     },
60401
60402         //private
60403     isSelectable : function(rowIndex, colIndex, cm){
60404         return !cm.isHidden(colIndex);
60405     },
60406
60407     /** @ignore */
60408     handleKeyDown : function(e){
60409         //Roo.log('Cell Sel Model handleKeyDown');
60410         if(!e.isNavKeyPress()){
60411             return;
60412         }
60413         var g = this.grid, s = this.selection;
60414         if(!s){
60415             e.stopEvent();
60416             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60417             if(cell){
60418                 this.select(cell[0], cell[1]);
60419             }
60420             return;
60421         }
60422         var sm = this;
60423         var walk = function(row, col, step){
60424             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60425         };
60426         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60427         var newCell;
60428
60429       
60430
60431         switch(k){
60432             case e.TAB:
60433                 // handled by onEditorKey
60434                 if (g.isEditor && g.editing) {
60435                     return;
60436                 }
60437                 if(e.shiftKey) {
60438                     newCell = walk(r, c-1, -1);
60439                 } else {
60440                     newCell = walk(r, c+1, 1);
60441                 }
60442                 break;
60443             
60444             case e.DOWN:
60445                newCell = walk(r+1, c, 1);
60446                 break;
60447             
60448             case e.UP:
60449                 newCell = walk(r-1, c, -1);
60450                 break;
60451             
60452             case e.RIGHT:
60453                 newCell = walk(r, c+1, 1);
60454                 break;
60455             
60456             case e.LEFT:
60457                 newCell = walk(r, c-1, -1);
60458                 break;
60459             
60460             case e.ENTER:
60461                 
60462                 if(g.isEditor && !g.editing){
60463                    g.startEditing(r, c);
60464                    e.stopEvent();
60465                    return;
60466                 }
60467                 
60468                 
60469              break;
60470         };
60471         if(newCell){
60472             this.select(newCell[0], newCell[1]);
60473             e.stopEvent();
60474             
60475         }
60476     },
60477
60478     acceptsNav : function(row, col, cm){
60479         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60480     },
60481     /**
60482      * Selects a cell.
60483      * @param {Number} field (not used) - as it's normally used as a listener
60484      * @param {Number} e - event - fake it by using
60485      *
60486      * var e = Roo.EventObjectImpl.prototype;
60487      * e.keyCode = e.TAB
60488      *
60489      * 
60490      */
60491     onEditorKey : function(field, e){
60492         
60493         var k = e.getKey(),
60494             newCell,
60495             g = this.grid,
60496             ed = g.activeEditor,
60497             forward = false;
60498         ///Roo.log('onEditorKey' + k);
60499         
60500         
60501         if (this.enter_is_tab && k == e.ENTER) {
60502             k = e.TAB;
60503         }
60504         
60505         if(k == e.TAB){
60506             if(e.shiftKey){
60507                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60508             }else{
60509                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60510                 forward = true;
60511             }
60512             
60513             e.stopEvent();
60514             
60515         } else if(k == e.ENTER &&  !e.ctrlKey){
60516             ed.completeEdit();
60517             e.stopEvent();
60518             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60519         
60520                 } else if(k == e.ESC){
60521             ed.cancelEdit();
60522         }
60523                 
60524         if (newCell) {
60525             var ecall = { cell : newCell, forward : forward };
60526             this.fireEvent('beforeeditnext', ecall );
60527             newCell = ecall.cell;
60528                         forward = ecall.forward;
60529         }
60530                 
60531         if(newCell){
60532             //Roo.log('next cell after edit');
60533             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
60534         } else if (forward) {
60535             // tabbed past last
60536             this.fireEvent.defer(100, this, ['tabend',this]);
60537         }
60538     }
60539 });/*
60540  * Based on:
60541  * Ext JS Library 1.1.1
60542  * Copyright(c) 2006-2007, Ext JS, LLC.
60543  *
60544  * Originally Released Under LGPL - original licence link has changed is not relivant.
60545  *
60546  * Fork - LGPL
60547  * <script type="text/javascript">
60548  */
60549  
60550 /**
60551  * @class Roo.grid.EditorGrid
60552  * @extends Roo.grid.Grid
60553  * Class for creating and editable grid.
60554  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
60555  * The container MUST have some type of size defined for the grid to fill. The container will be 
60556  * automatically set to position relative if it isn't already.
60557  * @param {Object} dataSource The data model to bind to
60558  * @param {Object} colModel The column model with info about this grid's columns
60559  */
60560 Roo.grid.EditorGrid = function(container, config){
60561     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
60562     this.getGridEl().addClass("xedit-grid");
60563
60564     if(!this.selModel){
60565         this.selModel = new Roo.grid.CellSelectionModel();
60566     }
60567
60568     this.activeEditor = null;
60569
60570         this.addEvents({
60571             /**
60572              * @event beforeedit
60573              * Fires before cell editing is triggered. The edit event object has the following properties <br />
60574              * <ul style="padding:5px;padding-left:16px;">
60575              * <li>grid - This grid</li>
60576              * <li>record - The record being edited</li>
60577              * <li>field - The field name being edited</li>
60578              * <li>value - The value for the field being edited.</li>
60579              * <li>row - The grid row index</li>
60580              * <li>column - The grid column index</li>
60581              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60582              * </ul>
60583              * @param {Object} e An edit event (see above for description)
60584              */
60585             "beforeedit" : true,
60586             /**
60587              * @event afteredit
60588              * Fires after a cell is edited. <br />
60589              * <ul style="padding:5px;padding-left:16px;">
60590              * <li>grid - This grid</li>
60591              * <li>record - The record being edited</li>
60592              * <li>field - The field name being edited</li>
60593              * <li>value - The value being set</li>
60594              * <li>originalValue - The original value for the field, before the edit.</li>
60595              * <li>row - The grid row index</li>
60596              * <li>column - The grid column index</li>
60597              * </ul>
60598              * @param {Object} e An edit event (see above for description)
60599              */
60600             "afteredit" : true,
60601             /**
60602              * @event validateedit
60603              * Fires after a cell is edited, but before the value is set in the record. 
60604          * You can use this to modify the value being set in the field, Return false
60605              * to cancel the change. The edit event object has the following properties <br />
60606              * <ul style="padding:5px;padding-left:16px;">
60607          * <li>editor - This editor</li>
60608              * <li>grid - This grid</li>
60609              * <li>record - The record being edited</li>
60610              * <li>field - The field name being edited</li>
60611              * <li>value - The value being set</li>
60612              * <li>originalValue - The original value for the field, before the edit.</li>
60613              * <li>row - The grid row index</li>
60614              * <li>column - The grid column index</li>
60615              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60616              * </ul>
60617              * @param {Object} e An edit event (see above for description)
60618              */
60619             "validateedit" : true
60620         });
60621     this.on("bodyscroll", this.stopEditing,  this);
60622     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
60623 };
60624
60625 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
60626     /**
60627      * @cfg {Number} clicksToEdit
60628      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
60629      */
60630     clicksToEdit: 2,
60631
60632     // private
60633     isEditor : true,
60634     // private
60635     trackMouseOver: false, // causes very odd FF errors
60636
60637     onCellDblClick : function(g, row, col){
60638         this.startEditing(row, col);
60639     },
60640
60641     onEditComplete : function(ed, value, startValue){
60642         this.editing = false;
60643         this.activeEditor = null;
60644         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
60645         var r = ed.record;
60646         var field = this.colModel.getDataIndex(ed.col);
60647         var e = {
60648             grid: this,
60649             record: r,
60650             field: field,
60651             originalValue: startValue,
60652             value: value,
60653             row: ed.row,
60654             column: ed.col,
60655             cancel:false,
60656             editor: ed
60657         };
60658         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
60659         cell.show();
60660           
60661         if(String(value) !== String(startValue)){
60662             
60663             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
60664                 r.set(field, e.value);
60665                 // if we are dealing with a combo box..
60666                 // then we also set the 'name' colum to be the displayField
60667                 if (ed.field.displayField && ed.field.name) {
60668                     r.set(ed.field.name, ed.field.el.dom.value);
60669                 }
60670                 
60671                 delete e.cancel; //?? why!!!
60672                 this.fireEvent("afteredit", e);
60673             }
60674         } else {
60675             this.fireEvent("afteredit", e); // always fire it!
60676         }
60677         this.view.focusCell(ed.row, ed.col);
60678     },
60679
60680     /**
60681      * Starts editing the specified for the specified row/column
60682      * @param {Number} rowIndex
60683      * @param {Number} colIndex
60684      */
60685     startEditing : function(row, col){
60686         this.stopEditing();
60687         if(this.colModel.isCellEditable(col, row)){
60688             this.view.ensureVisible(row, col, true);
60689           
60690             var r = this.dataSource.getAt(row);
60691             var field = this.colModel.getDataIndex(col);
60692             var cell = Roo.get(this.view.getCell(row,col));
60693             var e = {
60694                 grid: this,
60695                 record: r,
60696                 field: field,
60697                 value: r.data[field],
60698                 row: row,
60699                 column: col,
60700                 cancel:false 
60701             };
60702             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
60703                 this.editing = true;
60704                 var ed = this.colModel.getCellEditor(col, row);
60705                 
60706                 if (!ed) {
60707                     return;
60708                 }
60709                 if(!ed.rendered){
60710                     ed.render(ed.parentEl || document.body);
60711                 }
60712                 ed.field.reset();
60713                
60714                 cell.hide();
60715                 
60716                 (function(){ // complex but required for focus issues in safari, ie and opera
60717                     ed.row = row;
60718                     ed.col = col;
60719                     ed.record = r;
60720                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
60721                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60722                     this.activeEditor = ed;
60723                     var v = r.data[field];
60724                     ed.startEdit(this.view.getCell(row, col), v);
60725                     // combo's with 'displayField and name set
60726                     if (ed.field.displayField && ed.field.name) {
60727                         ed.field.el.dom.value = r.data[ed.field.name];
60728                     }
60729                     
60730                     
60731                 }).defer(50, this);
60732             }
60733         }
60734     },
60735         
60736     /**
60737      * Stops any active editing
60738      */
60739     stopEditing : function(){
60740         if(this.activeEditor){
60741             this.activeEditor.completeEdit();
60742         }
60743         this.activeEditor = null;
60744     },
60745         
60746          /**
60747      * Called to get grid's drag proxy text, by default returns this.ddText.
60748      * @return {String}
60749      */
60750     getDragDropText : function(){
60751         var count = this.selModel.getSelectedCell() ? 1 : 0;
60752         return String.format(this.ddText, count, count == 1 ? '' : 's');
60753     }
60754         
60755 });/*
60756  * Based on:
60757  * Ext JS Library 1.1.1
60758  * Copyright(c) 2006-2007, Ext JS, LLC.
60759  *
60760  * Originally Released Under LGPL - original licence link has changed is not relivant.
60761  *
60762  * Fork - LGPL
60763  * <script type="text/javascript">
60764  */
60765
60766 // private - not really -- you end up using it !
60767 // This is a support class used internally by the Grid components
60768
60769 /**
60770  * @class Roo.grid.GridEditor
60771  * @extends Roo.Editor
60772  * Class for creating and editable grid elements.
60773  * @param {Object} config any settings (must include field)
60774  */
60775 Roo.grid.GridEditor = function(field, config){
60776     if (!config && field.field) {
60777         config = field;
60778         field = Roo.factory(config.field, Roo.form);
60779     }
60780     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60781     field.monitorTab = false;
60782 };
60783
60784 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60785     
60786     /**
60787      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60788      */
60789     
60790     alignment: "tl-tl",
60791     autoSize: "width",
60792     hideEl : false,
60793     cls: "x-small-editor x-grid-editor",
60794     shim:false,
60795     shadow:"frame"
60796 });/*
60797  * Based on:
60798  * Ext JS Library 1.1.1
60799  * Copyright(c) 2006-2007, Ext JS, LLC.
60800  *
60801  * Originally Released Under LGPL - original licence link has changed is not relivant.
60802  *
60803  * Fork - LGPL
60804  * <script type="text/javascript">
60805  */
60806   
60807
60808   
60809 Roo.grid.PropertyRecord = Roo.data.Record.create([
60810     {name:'name',type:'string'},  'value'
60811 ]);
60812
60813
60814 Roo.grid.PropertyStore = function(grid, source){
60815     this.grid = grid;
60816     this.store = new Roo.data.Store({
60817         recordType : Roo.grid.PropertyRecord
60818     });
60819     this.store.on('update', this.onUpdate,  this);
60820     if(source){
60821         this.setSource(source);
60822     }
60823     Roo.grid.PropertyStore.superclass.constructor.call(this);
60824 };
60825
60826
60827
60828 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60829     setSource : function(o){
60830         this.source = o;
60831         this.store.removeAll();
60832         var data = [];
60833         for(var k in o){
60834             if(this.isEditableValue(o[k])){
60835                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60836             }
60837         }
60838         this.store.loadRecords({records: data}, {}, true);
60839     },
60840
60841     onUpdate : function(ds, record, type){
60842         if(type == Roo.data.Record.EDIT){
60843             var v = record.data['value'];
60844             var oldValue = record.modified['value'];
60845             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60846                 this.source[record.id] = v;
60847                 record.commit();
60848                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60849             }else{
60850                 record.reject();
60851             }
60852         }
60853     },
60854
60855     getProperty : function(row){
60856        return this.store.getAt(row);
60857     },
60858
60859     isEditableValue: function(val){
60860         if(val && val instanceof Date){
60861             return true;
60862         }else if(typeof val == 'object' || typeof val == 'function'){
60863             return false;
60864         }
60865         return true;
60866     },
60867
60868     setValue : function(prop, value){
60869         this.source[prop] = value;
60870         this.store.getById(prop).set('value', value);
60871     },
60872
60873     getSource : function(){
60874         return this.source;
60875     }
60876 });
60877
60878 Roo.grid.PropertyColumnModel = function(grid, store){
60879     this.grid = grid;
60880     var g = Roo.grid;
60881     g.PropertyColumnModel.superclass.constructor.call(this, [
60882         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60883         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60884     ]);
60885     this.store = store;
60886     this.bselect = Roo.DomHelper.append(document.body, {
60887         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60888             {tag: 'option', value: 'true', html: 'true'},
60889             {tag: 'option', value: 'false', html: 'false'}
60890         ]
60891     });
60892     Roo.id(this.bselect);
60893     var f = Roo.form;
60894     this.editors = {
60895         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60896         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60897         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60898         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60899         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60900     };
60901     this.renderCellDelegate = this.renderCell.createDelegate(this);
60902     this.renderPropDelegate = this.renderProp.createDelegate(this);
60903 };
60904
60905 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60906     
60907     
60908     nameText : 'Name',
60909     valueText : 'Value',
60910     
60911     dateFormat : 'm/j/Y',
60912     
60913     
60914     renderDate : function(dateVal){
60915         return dateVal.dateFormat(this.dateFormat);
60916     },
60917
60918     renderBool : function(bVal){
60919         return bVal ? 'true' : 'false';
60920     },
60921
60922     isCellEditable : function(colIndex, rowIndex){
60923         return colIndex == 1;
60924     },
60925
60926     getRenderer : function(col){
60927         return col == 1 ?
60928             this.renderCellDelegate : this.renderPropDelegate;
60929     },
60930
60931     renderProp : function(v){
60932         return this.getPropertyName(v);
60933     },
60934
60935     renderCell : function(val){
60936         var rv = val;
60937         if(val instanceof Date){
60938             rv = this.renderDate(val);
60939         }else if(typeof val == 'boolean'){
60940             rv = this.renderBool(val);
60941         }
60942         return Roo.util.Format.htmlEncode(rv);
60943     },
60944
60945     getPropertyName : function(name){
60946         var pn = this.grid.propertyNames;
60947         return pn && pn[name] ? pn[name] : name;
60948     },
60949
60950     getCellEditor : function(colIndex, rowIndex){
60951         var p = this.store.getProperty(rowIndex);
60952         var n = p.data['name'], val = p.data['value'];
60953         
60954         if(typeof(this.grid.customEditors[n]) == 'string'){
60955             return this.editors[this.grid.customEditors[n]];
60956         }
60957         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60958             return this.grid.customEditors[n];
60959         }
60960         if(val instanceof Date){
60961             return this.editors['date'];
60962         }else if(typeof val == 'number'){
60963             return this.editors['number'];
60964         }else if(typeof val == 'boolean'){
60965             return this.editors['boolean'];
60966         }else{
60967             return this.editors['string'];
60968         }
60969     }
60970 });
60971
60972 /**
60973  * @class Roo.grid.PropertyGrid
60974  * @extends Roo.grid.EditorGrid
60975  * This class represents the  interface of a component based property grid control.
60976  * <br><br>Usage:<pre><code>
60977  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60978       
60979  });
60980  // set any options
60981  grid.render();
60982  * </code></pre>
60983   
60984  * @constructor
60985  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60986  * The container MUST have some type of size defined for the grid to fill. The container will be
60987  * automatically set to position relative if it isn't already.
60988  * @param {Object} config A config object that sets properties on this grid.
60989  */
60990 Roo.grid.PropertyGrid = function(container, config){
60991     config = config || {};
60992     var store = new Roo.grid.PropertyStore(this);
60993     this.store = store;
60994     var cm = new Roo.grid.PropertyColumnModel(this, store);
60995     store.store.sort('name', 'ASC');
60996     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60997         ds: store.store,
60998         cm: cm,
60999         enableColLock:false,
61000         enableColumnMove:false,
61001         stripeRows:false,
61002         trackMouseOver: false,
61003         clicksToEdit:1
61004     }, config));
61005     this.getGridEl().addClass('x-props-grid');
61006     this.lastEditRow = null;
61007     this.on('columnresize', this.onColumnResize, this);
61008     this.addEvents({
61009          /**
61010              * @event beforepropertychange
61011              * Fires before a property changes (return false to stop?)
61012              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61013              * @param {String} id Record Id
61014              * @param {String} newval New Value
61015          * @param {String} oldval Old Value
61016              */
61017         "beforepropertychange": true,
61018         /**
61019              * @event propertychange
61020              * Fires after a property changes
61021              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61022              * @param {String} id Record Id
61023              * @param {String} newval New Value
61024          * @param {String} oldval Old Value
61025              */
61026         "propertychange": true
61027     });
61028     this.customEditors = this.customEditors || {};
61029 };
61030 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61031     
61032      /**
61033      * @cfg {Object} customEditors map of colnames=> custom editors.
61034      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61035      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61036      * false disables editing of the field.
61037          */
61038     
61039       /**
61040      * @cfg {Object} propertyNames map of property Names to their displayed value
61041          */
61042     
61043     render : function(){
61044         Roo.grid.PropertyGrid.superclass.render.call(this);
61045         this.autoSize.defer(100, this);
61046     },
61047
61048     autoSize : function(){
61049         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61050         if(this.view){
61051             this.view.fitColumns();
61052         }
61053     },
61054
61055     onColumnResize : function(){
61056         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61057         this.autoSize();
61058     },
61059     /**
61060      * Sets the data for the Grid
61061      * accepts a Key => Value object of all the elements avaiable.
61062      * @param {Object} data  to appear in grid.
61063      */
61064     setSource : function(source){
61065         this.store.setSource(source);
61066         //this.autoSize();
61067     },
61068     /**
61069      * Gets all the data from the grid.
61070      * @return {Object} data  data stored in grid
61071      */
61072     getSource : function(){
61073         return this.store.getSource();
61074     }
61075 });/*
61076   
61077  * Licence LGPL
61078  
61079  */
61080  
61081 /**
61082  * @class Roo.grid.Calendar
61083  * @extends Roo.grid.Grid
61084  * This class extends the Grid to provide a calendar widget
61085  * <br><br>Usage:<pre><code>
61086  var grid = new Roo.grid.Calendar("my-container-id", {
61087      ds: myDataStore,
61088      cm: myColModel,
61089      selModel: mySelectionModel,
61090      autoSizeColumns: true,
61091      monitorWindowResize: false,
61092      trackMouseOver: true
61093      eventstore : real data store..
61094  });
61095  // set any options
61096  grid.render();
61097   
61098   * @constructor
61099  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61100  * The container MUST have some type of size defined for the grid to fill. The container will be
61101  * automatically set to position relative if it isn't already.
61102  * @param {Object} config A config object that sets properties on this grid.
61103  */
61104 Roo.grid.Calendar = function(container, config){
61105         // initialize the container
61106         this.container = Roo.get(container);
61107         this.container.update("");
61108         this.container.setStyle("overflow", "hidden");
61109     this.container.addClass('x-grid-container');
61110
61111     this.id = this.container.id;
61112
61113     Roo.apply(this, config);
61114     // check and correct shorthanded configs
61115     
61116     var rows = [];
61117     var d =1;
61118     for (var r = 0;r < 6;r++) {
61119         
61120         rows[r]=[];
61121         for (var c =0;c < 7;c++) {
61122             rows[r][c]= '';
61123         }
61124     }
61125     if (this.eventStore) {
61126         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61127         this.eventStore.on('load',this.onLoad, this);
61128         this.eventStore.on('beforeload',this.clearEvents, this);
61129          
61130     }
61131     
61132     this.dataSource = new Roo.data.Store({
61133             proxy: new Roo.data.MemoryProxy(rows),
61134             reader: new Roo.data.ArrayReader({}, [
61135                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61136     });
61137
61138     this.dataSource.load();
61139     this.ds = this.dataSource;
61140     this.ds.xmodule = this.xmodule || false;
61141     
61142     
61143     var cellRender = function(v,x,r)
61144     {
61145         return String.format(
61146             '<div class="fc-day  fc-widget-content"><div>' +
61147                 '<div class="fc-event-container"></div>' +
61148                 '<div class="fc-day-number">{0}</div>'+
61149                 
61150                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61151             '</div></div>', v);
61152     
61153     }
61154     
61155     
61156     this.colModel = new Roo.grid.ColumnModel( [
61157         {
61158             xtype: 'ColumnModel',
61159             xns: Roo.grid,
61160             dataIndex : 'weekday0',
61161             header : 'Sunday',
61162             renderer : cellRender
61163         },
61164         {
61165             xtype: 'ColumnModel',
61166             xns: Roo.grid,
61167             dataIndex : 'weekday1',
61168             header : 'Monday',
61169             renderer : cellRender
61170         },
61171         {
61172             xtype: 'ColumnModel',
61173             xns: Roo.grid,
61174             dataIndex : 'weekday2',
61175             header : 'Tuesday',
61176             renderer : cellRender
61177         },
61178         {
61179             xtype: 'ColumnModel',
61180             xns: Roo.grid,
61181             dataIndex : 'weekday3',
61182             header : 'Wednesday',
61183             renderer : cellRender
61184         },
61185         {
61186             xtype: 'ColumnModel',
61187             xns: Roo.grid,
61188             dataIndex : 'weekday4',
61189             header : 'Thursday',
61190             renderer : cellRender
61191         },
61192         {
61193             xtype: 'ColumnModel',
61194             xns: Roo.grid,
61195             dataIndex : 'weekday5',
61196             header : 'Friday',
61197             renderer : cellRender
61198         },
61199         {
61200             xtype: 'ColumnModel',
61201             xns: Roo.grid,
61202             dataIndex : 'weekday6',
61203             header : 'Saturday',
61204             renderer : cellRender
61205         }
61206     ]);
61207     this.cm = this.colModel;
61208     this.cm.xmodule = this.xmodule || false;
61209  
61210         
61211           
61212     //this.selModel = new Roo.grid.CellSelectionModel();
61213     //this.sm = this.selModel;
61214     //this.selModel.init(this);
61215     
61216     
61217     if(this.width){
61218         this.container.setWidth(this.width);
61219     }
61220
61221     if(this.height){
61222         this.container.setHeight(this.height);
61223     }
61224     /** @private */
61225         this.addEvents({
61226         // raw events
61227         /**
61228          * @event click
61229          * The raw click event for the entire grid.
61230          * @param {Roo.EventObject} e
61231          */
61232         "click" : true,
61233         /**
61234          * @event dblclick
61235          * The raw dblclick event for the entire grid.
61236          * @param {Roo.EventObject} e
61237          */
61238         "dblclick" : true,
61239         /**
61240          * @event contextmenu
61241          * The raw contextmenu event for the entire grid.
61242          * @param {Roo.EventObject} e
61243          */
61244         "contextmenu" : true,
61245         /**
61246          * @event mousedown
61247          * The raw mousedown event for the entire grid.
61248          * @param {Roo.EventObject} e
61249          */
61250         "mousedown" : true,
61251         /**
61252          * @event mouseup
61253          * The raw mouseup event for the entire grid.
61254          * @param {Roo.EventObject} e
61255          */
61256         "mouseup" : true,
61257         /**
61258          * @event mouseover
61259          * The raw mouseover event for the entire grid.
61260          * @param {Roo.EventObject} e
61261          */
61262         "mouseover" : true,
61263         /**
61264          * @event mouseout
61265          * The raw mouseout event for the entire grid.
61266          * @param {Roo.EventObject} e
61267          */
61268         "mouseout" : true,
61269         /**
61270          * @event keypress
61271          * The raw keypress event for the entire grid.
61272          * @param {Roo.EventObject} e
61273          */
61274         "keypress" : true,
61275         /**
61276          * @event keydown
61277          * The raw keydown event for the entire grid.
61278          * @param {Roo.EventObject} e
61279          */
61280         "keydown" : true,
61281
61282         // custom events
61283
61284         /**
61285          * @event cellclick
61286          * Fires when a cell is clicked
61287          * @param {Grid} this
61288          * @param {Number} rowIndex
61289          * @param {Number} columnIndex
61290          * @param {Roo.EventObject} e
61291          */
61292         "cellclick" : true,
61293         /**
61294          * @event celldblclick
61295          * Fires when a cell is double clicked
61296          * @param {Grid} this
61297          * @param {Number} rowIndex
61298          * @param {Number} columnIndex
61299          * @param {Roo.EventObject} e
61300          */
61301         "celldblclick" : true,
61302         /**
61303          * @event rowclick
61304          * Fires when a row is clicked
61305          * @param {Grid} this
61306          * @param {Number} rowIndex
61307          * @param {Roo.EventObject} e
61308          */
61309         "rowclick" : true,
61310         /**
61311          * @event rowdblclick
61312          * Fires when a row is double clicked
61313          * @param {Grid} this
61314          * @param {Number} rowIndex
61315          * @param {Roo.EventObject} e
61316          */
61317         "rowdblclick" : true,
61318         /**
61319          * @event headerclick
61320          * Fires when a header is clicked
61321          * @param {Grid} this
61322          * @param {Number} columnIndex
61323          * @param {Roo.EventObject} e
61324          */
61325         "headerclick" : true,
61326         /**
61327          * @event headerdblclick
61328          * Fires when a header cell is double clicked
61329          * @param {Grid} this
61330          * @param {Number} columnIndex
61331          * @param {Roo.EventObject} e
61332          */
61333         "headerdblclick" : true,
61334         /**
61335          * @event rowcontextmenu
61336          * Fires when a row is right clicked
61337          * @param {Grid} this
61338          * @param {Number} rowIndex
61339          * @param {Roo.EventObject} e
61340          */
61341         "rowcontextmenu" : true,
61342         /**
61343          * @event cellcontextmenu
61344          * Fires when a cell is right clicked
61345          * @param {Grid} this
61346          * @param {Number} rowIndex
61347          * @param {Number} cellIndex
61348          * @param {Roo.EventObject} e
61349          */
61350          "cellcontextmenu" : true,
61351         /**
61352          * @event headercontextmenu
61353          * Fires when a header is right clicked
61354          * @param {Grid} this
61355          * @param {Number} columnIndex
61356          * @param {Roo.EventObject} e
61357          */
61358         "headercontextmenu" : true,
61359         /**
61360          * @event bodyscroll
61361          * Fires when the body element is scrolled
61362          * @param {Number} scrollLeft
61363          * @param {Number} scrollTop
61364          */
61365         "bodyscroll" : true,
61366         /**
61367          * @event columnresize
61368          * Fires when the user resizes a column
61369          * @param {Number} columnIndex
61370          * @param {Number} newSize
61371          */
61372         "columnresize" : true,
61373         /**
61374          * @event columnmove
61375          * Fires when the user moves a column
61376          * @param {Number} oldIndex
61377          * @param {Number} newIndex
61378          */
61379         "columnmove" : true,
61380         /**
61381          * @event startdrag
61382          * Fires when row(s) start being dragged
61383          * @param {Grid} this
61384          * @param {Roo.GridDD} dd The drag drop object
61385          * @param {event} e The raw browser event
61386          */
61387         "startdrag" : true,
61388         /**
61389          * @event enddrag
61390          * Fires when a drag operation is complete
61391          * @param {Grid} this
61392          * @param {Roo.GridDD} dd The drag drop object
61393          * @param {event} e The raw browser event
61394          */
61395         "enddrag" : true,
61396         /**
61397          * @event dragdrop
61398          * Fires when dragged row(s) are dropped on a valid DD target
61399          * @param {Grid} this
61400          * @param {Roo.GridDD} dd The drag drop object
61401          * @param {String} targetId The target drag drop object
61402          * @param {event} e The raw browser event
61403          */
61404         "dragdrop" : true,
61405         /**
61406          * @event dragover
61407          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61408          * @param {Grid} this
61409          * @param {Roo.GridDD} dd The drag drop object
61410          * @param {String} targetId The target drag drop object
61411          * @param {event} e The raw browser event
61412          */
61413         "dragover" : true,
61414         /**
61415          * @event dragenter
61416          *  Fires when the dragged row(s) first cross another DD target while being dragged
61417          * @param {Grid} this
61418          * @param {Roo.GridDD} dd The drag drop object
61419          * @param {String} targetId The target drag drop object
61420          * @param {event} e The raw browser event
61421          */
61422         "dragenter" : true,
61423         /**
61424          * @event dragout
61425          * Fires when the dragged row(s) leave another DD target while being dragged
61426          * @param {Grid} this
61427          * @param {Roo.GridDD} dd The drag drop object
61428          * @param {String} targetId The target drag drop object
61429          * @param {event} e The raw browser event
61430          */
61431         "dragout" : true,
61432         /**
61433          * @event rowclass
61434          * Fires when a row is rendered, so you can change add a style to it.
61435          * @param {GridView} gridview   The grid view
61436          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61437          */
61438         'rowclass' : true,
61439
61440         /**
61441          * @event render
61442          * Fires when the grid is rendered
61443          * @param {Grid} grid
61444          */
61445         'render' : true,
61446             /**
61447              * @event select
61448              * Fires when a date is selected
61449              * @param {DatePicker} this
61450              * @param {Date} date The selected date
61451              */
61452         'select': true,
61453         /**
61454              * @event monthchange
61455              * Fires when the displayed month changes 
61456              * @param {DatePicker} this
61457              * @param {Date} date The selected month
61458              */
61459         'monthchange': true,
61460         /**
61461              * @event evententer
61462              * Fires when mouse over an event
61463              * @param {Calendar} this
61464              * @param {event} Event
61465              */
61466         'evententer': true,
61467         /**
61468              * @event eventleave
61469              * Fires when the mouse leaves an
61470              * @param {Calendar} this
61471              * @param {event}
61472              */
61473         'eventleave': true,
61474         /**
61475              * @event eventclick
61476              * Fires when the mouse click an
61477              * @param {Calendar} this
61478              * @param {event}
61479              */
61480         'eventclick': true,
61481         /**
61482              * @event eventrender
61483              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
61484              * @param {Calendar} this
61485              * @param {data} data to be modified
61486              */
61487         'eventrender': true
61488         
61489     });
61490
61491     Roo.grid.Grid.superclass.constructor.call(this);
61492     this.on('render', function() {
61493         this.view.el.addClass('x-grid-cal'); 
61494         
61495         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
61496
61497     },this);
61498     
61499     if (!Roo.grid.Calendar.style) {
61500         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
61501             
61502             
61503             '.x-grid-cal .x-grid-col' :  {
61504                 height: 'auto !important',
61505                 'vertical-align': 'top'
61506             },
61507             '.x-grid-cal  .fc-event-hori' : {
61508                 height: '14px'
61509             }
61510              
61511             
61512         }, Roo.id());
61513     }
61514
61515     
61516     
61517 };
61518 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
61519     /**
61520      * @cfg {Store} eventStore The store that loads events.
61521      */
61522     eventStore : 25,
61523
61524      
61525     activeDate : false,
61526     startDay : 0,
61527     autoWidth : true,
61528     monitorWindowResize : false,
61529
61530     
61531     resizeColumns : function() {
61532         var col = (this.view.el.getWidth() / 7) - 3;
61533         // loop through cols, and setWidth
61534         for(var i =0 ; i < 7 ; i++){
61535             this.cm.setColumnWidth(i, col);
61536         }
61537     },
61538      setDate :function(date) {
61539         
61540         Roo.log('setDate?');
61541         
61542         this.resizeColumns();
61543         var vd = this.activeDate;
61544         this.activeDate = date;
61545 //        if(vd && this.el){
61546 //            var t = date.getTime();
61547 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
61548 //                Roo.log('using add remove');
61549 //                
61550 //                this.fireEvent('monthchange', this, date);
61551 //                
61552 //                this.cells.removeClass("fc-state-highlight");
61553 //                this.cells.each(function(c){
61554 //                   if(c.dateValue == t){
61555 //                       c.addClass("fc-state-highlight");
61556 //                       setTimeout(function(){
61557 //                            try{c.dom.firstChild.focus();}catch(e){}
61558 //                       }, 50);
61559 //                       return false;
61560 //                   }
61561 //                   return true;
61562 //                });
61563 //                return;
61564 //            }
61565 //        }
61566         
61567         var days = date.getDaysInMonth();
61568         
61569         var firstOfMonth = date.getFirstDateOfMonth();
61570         var startingPos = firstOfMonth.getDay()-this.startDay;
61571         
61572         if(startingPos < this.startDay){
61573             startingPos += 7;
61574         }
61575         
61576         var pm = date.add(Date.MONTH, -1);
61577         var prevStart = pm.getDaysInMonth()-startingPos;
61578 //        
61579         
61580         
61581         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61582         
61583         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
61584         //this.cells.addClassOnOver('fc-state-hover');
61585         
61586         var cells = this.cells.elements;
61587         var textEls = this.textNodes;
61588         
61589         //Roo.each(cells, function(cell){
61590         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
61591         //});
61592         
61593         days += startingPos;
61594
61595         // convert everything to numbers so it's fast
61596         var day = 86400000;
61597         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
61598         //Roo.log(d);
61599         //Roo.log(pm);
61600         //Roo.log(prevStart);
61601         
61602         var today = new Date().clearTime().getTime();
61603         var sel = date.clearTime().getTime();
61604         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
61605         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
61606         var ddMatch = this.disabledDatesRE;
61607         var ddText = this.disabledDatesText;
61608         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
61609         var ddaysText = this.disabledDaysText;
61610         var format = this.format;
61611         
61612         var setCellClass = function(cal, cell){
61613             
61614             //Roo.log('set Cell Class');
61615             cell.title = "";
61616             var t = d.getTime();
61617             
61618             //Roo.log(d);
61619             
61620             
61621             cell.dateValue = t;
61622             if(t == today){
61623                 cell.className += " fc-today";
61624                 cell.className += " fc-state-highlight";
61625                 cell.title = cal.todayText;
61626             }
61627             if(t == sel){
61628                 // disable highlight in other month..
61629                 cell.className += " fc-state-highlight";
61630                 
61631             }
61632             // disabling
61633             if(t < min) {
61634                 //cell.className = " fc-state-disabled";
61635                 cell.title = cal.minText;
61636                 return;
61637             }
61638             if(t > max) {
61639                 //cell.className = " fc-state-disabled";
61640                 cell.title = cal.maxText;
61641                 return;
61642             }
61643             if(ddays){
61644                 if(ddays.indexOf(d.getDay()) != -1){
61645                     // cell.title = ddaysText;
61646                    // cell.className = " fc-state-disabled";
61647                 }
61648             }
61649             if(ddMatch && format){
61650                 var fvalue = d.dateFormat(format);
61651                 if(ddMatch.test(fvalue)){
61652                     cell.title = ddText.replace("%0", fvalue);
61653                    cell.className = " fc-state-disabled";
61654                 }
61655             }
61656             
61657             if (!cell.initialClassName) {
61658                 cell.initialClassName = cell.dom.className;
61659             }
61660             
61661             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
61662         };
61663
61664         var i = 0;
61665         
61666         for(; i < startingPos; i++) {
61667             cells[i].dayName =  (++prevStart);
61668             Roo.log(textEls[i]);
61669             d.setDate(d.getDate()+1);
61670             
61671             //cells[i].className = "fc-past fc-other-month";
61672             setCellClass(this, cells[i]);
61673         }
61674         
61675         var intDay = 0;
61676         
61677         for(; i < days; i++){
61678             intDay = i - startingPos + 1;
61679             cells[i].dayName =  (intDay);
61680             d.setDate(d.getDate()+1);
61681             
61682             cells[i].className = ''; // "x-date-active";
61683             setCellClass(this, cells[i]);
61684         }
61685         var extraDays = 0;
61686         
61687         for(; i < 42; i++) {
61688             //textEls[i].innerHTML = (++extraDays);
61689             
61690             d.setDate(d.getDate()+1);
61691             cells[i].dayName = (++extraDays);
61692             cells[i].className = "fc-future fc-other-month";
61693             setCellClass(this, cells[i]);
61694         }
61695         
61696         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
61697         
61698         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
61699         
61700         // this will cause all the cells to mis
61701         var rows= [];
61702         var i =0;
61703         for (var r = 0;r < 6;r++) {
61704             for (var c =0;c < 7;c++) {
61705                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
61706             }    
61707         }
61708         
61709         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61710         for(i=0;i<cells.length;i++) {
61711             
61712             this.cells.elements[i].dayName = cells[i].dayName ;
61713             this.cells.elements[i].className = cells[i].className;
61714             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
61715             this.cells.elements[i].title = cells[i].title ;
61716             this.cells.elements[i].dateValue = cells[i].dateValue ;
61717         }
61718         
61719         
61720         
61721         
61722         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61723         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61724         
61725         ////if(totalRows != 6){
61726             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61727            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61728        // }
61729         
61730         this.fireEvent('monthchange', this, date);
61731         
61732         
61733     },
61734  /**
61735      * Returns the grid's SelectionModel.
61736      * @return {SelectionModel}
61737      */
61738     getSelectionModel : function(){
61739         if(!this.selModel){
61740             this.selModel = new Roo.grid.CellSelectionModel();
61741         }
61742         return this.selModel;
61743     },
61744
61745     load: function() {
61746         this.eventStore.load()
61747         
61748         
61749         
61750     },
61751     
61752     findCell : function(dt) {
61753         dt = dt.clearTime().getTime();
61754         var ret = false;
61755         this.cells.each(function(c){
61756             //Roo.log("check " +c.dateValue + '?=' + dt);
61757             if(c.dateValue == dt){
61758                 ret = c;
61759                 return false;
61760             }
61761             return true;
61762         });
61763         
61764         return ret;
61765     },
61766     
61767     findCells : function(rec) {
61768         var s = rec.data.start_dt.clone().clearTime().getTime();
61769        // Roo.log(s);
61770         var e= rec.data.end_dt.clone().clearTime().getTime();
61771        // Roo.log(e);
61772         var ret = [];
61773         this.cells.each(function(c){
61774              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61775             
61776             if(c.dateValue > e){
61777                 return ;
61778             }
61779             if(c.dateValue < s){
61780                 return ;
61781             }
61782             ret.push(c);
61783         });
61784         
61785         return ret;    
61786     },
61787     
61788     findBestRow: function(cells)
61789     {
61790         var ret = 0;
61791         
61792         for (var i =0 ; i < cells.length;i++) {
61793             ret  = Math.max(cells[i].rows || 0,ret);
61794         }
61795         return ret;
61796         
61797     },
61798     
61799     
61800     addItem : function(rec)
61801     {
61802         // look for vertical location slot in
61803         var cells = this.findCells(rec);
61804         
61805         rec.row = this.findBestRow(cells);
61806         
61807         // work out the location.
61808         
61809         var crow = false;
61810         var rows = [];
61811         for(var i =0; i < cells.length; i++) {
61812             if (!crow) {
61813                 crow = {
61814                     start : cells[i],
61815                     end :  cells[i]
61816                 };
61817                 continue;
61818             }
61819             if (crow.start.getY() == cells[i].getY()) {
61820                 // on same row.
61821                 crow.end = cells[i];
61822                 continue;
61823             }
61824             // different row.
61825             rows.push(crow);
61826             crow = {
61827                 start: cells[i],
61828                 end : cells[i]
61829             };
61830             
61831         }
61832         
61833         rows.push(crow);
61834         rec.els = [];
61835         rec.rows = rows;
61836         rec.cells = cells;
61837         for (var i = 0; i < cells.length;i++) {
61838             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61839             
61840         }
61841         
61842         
61843     },
61844     
61845     clearEvents: function() {
61846         
61847         if (!this.eventStore.getCount()) {
61848             return;
61849         }
61850         // reset number of rows in cells.
61851         Roo.each(this.cells.elements, function(c){
61852             c.rows = 0;
61853         });
61854         
61855         this.eventStore.each(function(e) {
61856             this.clearEvent(e);
61857         },this);
61858         
61859     },
61860     
61861     clearEvent : function(ev)
61862     {
61863         if (ev.els) {
61864             Roo.each(ev.els, function(el) {
61865                 el.un('mouseenter' ,this.onEventEnter, this);
61866                 el.un('mouseleave' ,this.onEventLeave, this);
61867                 el.remove();
61868             },this);
61869             ev.els = [];
61870         }
61871     },
61872     
61873     
61874     renderEvent : function(ev,ctr) {
61875         if (!ctr) {
61876              ctr = this.view.el.select('.fc-event-container',true).first();
61877         }
61878         
61879          
61880         this.clearEvent(ev);
61881             //code
61882        
61883         
61884         
61885         ev.els = [];
61886         var cells = ev.cells;
61887         var rows = ev.rows;
61888         this.fireEvent('eventrender', this, ev);
61889         
61890         for(var i =0; i < rows.length; i++) {
61891             
61892             cls = '';
61893             if (i == 0) {
61894                 cls += ' fc-event-start';
61895             }
61896             if ((i+1) == rows.length) {
61897                 cls += ' fc-event-end';
61898             }
61899             
61900             //Roo.log(ev.data);
61901             // how many rows should it span..
61902             var cg = this.eventTmpl.append(ctr,Roo.apply({
61903                 fccls : cls
61904                 
61905             }, ev.data) , true);
61906             
61907             
61908             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61909             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61910             cg.on('click', this.onEventClick, this, ev);
61911             
61912             ev.els.push(cg);
61913             
61914             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61915             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61916             //Roo.log(cg);
61917              
61918             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61919             cg.setWidth(ebox.right - sbox.x -2);
61920         }
61921     },
61922     
61923     renderEvents: function()
61924     {   
61925         // first make sure there is enough space..
61926         
61927         if (!this.eventTmpl) {
61928             this.eventTmpl = new Roo.Template(
61929                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61930                     '<div class="fc-event-inner">' +
61931                         '<span class="fc-event-time">{time}</span>' +
61932                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61933                     '</div>' +
61934                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61935                 '</div>'
61936             );
61937                 
61938         }
61939                
61940         
61941         
61942         this.cells.each(function(c) {
61943             //Roo.log(c.select('.fc-day-content div',true).first());
61944             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61945         });
61946         
61947         var ctr = this.view.el.select('.fc-event-container',true).first();
61948         
61949         var cls;
61950         this.eventStore.each(function(ev){
61951             
61952             this.renderEvent(ev);
61953              
61954              
61955         }, this);
61956         this.view.layout();
61957         
61958     },
61959     
61960     onEventEnter: function (e, el,event,d) {
61961         this.fireEvent('evententer', this, el, event);
61962     },
61963     
61964     onEventLeave: function (e, el,event,d) {
61965         this.fireEvent('eventleave', this, el, event);
61966     },
61967     
61968     onEventClick: function (e, el,event,d) {
61969         this.fireEvent('eventclick', this, el, event);
61970     },
61971     
61972     onMonthChange: function () {
61973         this.store.load();
61974     },
61975     
61976     onLoad: function () {
61977         
61978         //Roo.log('calendar onload');
61979 //         
61980         if(this.eventStore.getCount() > 0){
61981             
61982            
61983             
61984             this.eventStore.each(function(d){
61985                 
61986                 
61987                 // FIXME..
61988                 var add =   d.data;
61989                 if (typeof(add.end_dt) == 'undefined')  {
61990                     Roo.log("Missing End time in calendar data: ");
61991                     Roo.log(d);
61992                     return;
61993                 }
61994                 if (typeof(add.start_dt) == 'undefined')  {
61995                     Roo.log("Missing Start time in calendar data: ");
61996                     Roo.log(d);
61997                     return;
61998                 }
61999                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62000                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62001                 add.id = add.id || d.id;
62002                 add.title = add.title || '??';
62003                 
62004                 this.addItem(d);
62005                 
62006              
62007             },this);
62008         }
62009         
62010         this.renderEvents();
62011     }
62012     
62013
62014 });
62015 /*
62016  grid : {
62017                 xtype: 'Grid',
62018                 xns: Roo.grid,
62019                 listeners : {
62020                     render : function ()
62021                     {
62022                         _this.grid = this;
62023                         
62024                         if (!this.view.el.hasClass('course-timesheet')) {
62025                             this.view.el.addClass('course-timesheet');
62026                         }
62027                         if (this.tsStyle) {
62028                             this.ds.load({});
62029                             return; 
62030                         }
62031                         Roo.log('width');
62032                         Roo.log(_this.grid.view.el.getWidth());
62033                         
62034                         
62035                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62036                             '.course-timesheet .x-grid-row' : {
62037                                 height: '80px'
62038                             },
62039                             '.x-grid-row td' : {
62040                                 'vertical-align' : 0
62041                             },
62042                             '.course-edit-link' : {
62043                                 'color' : 'blue',
62044                                 'text-overflow' : 'ellipsis',
62045                                 'overflow' : 'hidden',
62046                                 'white-space' : 'nowrap',
62047                                 'cursor' : 'pointer'
62048                             },
62049                             '.sub-link' : {
62050                                 'color' : 'green'
62051                             },
62052                             '.de-act-sup-link' : {
62053                                 'color' : 'purple',
62054                                 'text-decoration' : 'line-through'
62055                             },
62056                             '.de-act-link' : {
62057                                 'color' : 'red',
62058                                 'text-decoration' : 'line-through'
62059                             },
62060                             '.course-timesheet .course-highlight' : {
62061                                 'border-top-style': 'dashed !important',
62062                                 'border-bottom-bottom': 'dashed !important'
62063                             },
62064                             '.course-timesheet .course-item' : {
62065                                 'font-family'   : 'tahoma, arial, helvetica',
62066                                 'font-size'     : '11px',
62067                                 'overflow'      : 'hidden',
62068                                 'padding-left'  : '10px',
62069                                 'padding-right' : '10px',
62070                                 'padding-top' : '10px' 
62071                             }
62072                             
62073                         }, Roo.id());
62074                                 this.ds.load({});
62075                     }
62076                 },
62077                 autoWidth : true,
62078                 monitorWindowResize : false,
62079                 cellrenderer : function(v,x,r)
62080                 {
62081                     return v;
62082                 },
62083                 sm : {
62084                     xtype: 'CellSelectionModel',
62085                     xns: Roo.grid
62086                 },
62087                 dataSource : {
62088                     xtype: 'Store',
62089                     xns: Roo.data,
62090                     listeners : {
62091                         beforeload : function (_self, options)
62092                         {
62093                             options.params = options.params || {};
62094                             options.params._month = _this.monthField.getValue();
62095                             options.params.limit = 9999;
62096                             options.params['sort'] = 'when_dt';    
62097                             options.params['dir'] = 'ASC';    
62098                             this.proxy.loadResponse = this.loadResponse;
62099                             Roo.log("load?");
62100                             //this.addColumns();
62101                         },
62102                         load : function (_self, records, options)
62103                         {
62104                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62105                                 // if you click on the translation.. you can edit it...
62106                                 var el = Roo.get(this);
62107                                 var id = el.dom.getAttribute('data-id');
62108                                 var d = el.dom.getAttribute('data-date');
62109                                 var t = el.dom.getAttribute('data-time');
62110                                 //var id = this.child('span').dom.textContent;
62111                                 
62112                                 //Roo.log(this);
62113                                 Pman.Dialog.CourseCalendar.show({
62114                                     id : id,
62115                                     when_d : d,
62116                                     when_t : t,
62117                                     productitem_active : id ? 1 : 0
62118                                 }, function() {
62119                                     _this.grid.ds.load({});
62120                                 });
62121                            
62122                            });
62123                            
62124                            _this.panel.fireEvent('resize', [ '', '' ]);
62125                         }
62126                     },
62127                     loadResponse : function(o, success, response){
62128                             // this is overridden on before load..
62129                             
62130                             Roo.log("our code?");       
62131                             //Roo.log(success);
62132                             //Roo.log(response)
62133                             delete this.activeRequest;
62134                             if(!success){
62135                                 this.fireEvent("loadexception", this, o, response);
62136                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62137                                 return;
62138                             }
62139                             var result;
62140                             try {
62141                                 result = o.reader.read(response);
62142                             }catch(e){
62143                                 Roo.log("load exception?");
62144                                 this.fireEvent("loadexception", this, o, response, e);
62145                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62146                                 return;
62147                             }
62148                             Roo.log("ready...");        
62149                             // loop through result.records;
62150                             // and set this.tdate[date] = [] << array of records..
62151                             _this.tdata  = {};
62152                             Roo.each(result.records, function(r){
62153                                 //Roo.log(r.data);
62154                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62155                                     _this.tdata[r.data.when_dt.format('j')] = [];
62156                                 }
62157                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62158                             });
62159                             
62160                             //Roo.log(_this.tdata);
62161                             
62162                             result.records = [];
62163                             result.totalRecords = 6;
62164                     
62165                             // let's generate some duumy records for the rows.
62166                             //var st = _this.dateField.getValue();
62167                             
62168                             // work out monday..
62169                             //st = st.add(Date.DAY, -1 * st.format('w'));
62170                             
62171                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62172                             
62173                             var firstOfMonth = date.getFirstDayOfMonth();
62174                             var days = date.getDaysInMonth();
62175                             var d = 1;
62176                             var firstAdded = false;
62177                             for (var i = 0; i < result.totalRecords ; i++) {
62178                                 //var d= st.add(Date.DAY, i);
62179                                 var row = {};
62180                                 var added = 0;
62181                                 for(var w = 0 ; w < 7 ; w++){
62182                                     if(!firstAdded && firstOfMonth != w){
62183                                         continue;
62184                                     }
62185                                     if(d > days){
62186                                         continue;
62187                                     }
62188                                     firstAdded = true;
62189                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62190                                     row['weekday'+w] = String.format(
62191                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62192                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62193                                                     d,
62194                                                     date.format('Y-m-')+dd
62195                                                 );
62196                                     added++;
62197                                     if(typeof(_this.tdata[d]) != 'undefined'){
62198                                         Roo.each(_this.tdata[d], function(r){
62199                                             var is_sub = '';
62200                                             var deactive = '';
62201                                             var id = r.id;
62202                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62203                                             if(r.parent_id*1>0){
62204                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62205                                                 id = r.parent_id;
62206                                             }
62207                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62208                                                 deactive = 'de-act-link';
62209                                             }
62210                                             
62211                                             row['weekday'+w] += String.format(
62212                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62213                                                     id, //0
62214                                                     r.product_id_name, //1
62215                                                     r.when_dt.format('h:ia'), //2
62216                                                     is_sub, //3
62217                                                     deactive, //4
62218                                                     desc // 5
62219                                             );
62220                                         });
62221                                     }
62222                                     d++;
62223                                 }
62224                                 
62225                                 // only do this if something added..
62226                                 if(added > 0){ 
62227                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62228                                 }
62229                                 
62230                                 
62231                                 // push it twice. (second one with an hour..
62232                                 
62233                             }
62234                             //Roo.log(result);
62235                             this.fireEvent("load", this, o, o.request.arg);
62236                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62237                         },
62238                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62239                     proxy : {
62240                         xtype: 'HttpProxy',
62241                         xns: Roo.data,
62242                         method : 'GET',
62243                         url : baseURL + '/Roo/Shop_course.php'
62244                     },
62245                     reader : {
62246                         xtype: 'JsonReader',
62247                         xns: Roo.data,
62248                         id : 'id',
62249                         fields : [
62250                             {
62251                                 'name': 'id',
62252                                 'type': 'int'
62253                             },
62254                             {
62255                                 'name': 'when_dt',
62256                                 'type': 'string'
62257                             },
62258                             {
62259                                 'name': 'end_dt',
62260                                 'type': 'string'
62261                             },
62262                             {
62263                                 'name': 'parent_id',
62264                                 'type': 'int'
62265                             },
62266                             {
62267                                 'name': 'product_id',
62268                                 'type': 'int'
62269                             },
62270                             {
62271                                 'name': 'productitem_id',
62272                                 'type': 'int'
62273                             },
62274                             {
62275                                 'name': 'guid',
62276                                 'type': 'int'
62277                             }
62278                         ]
62279                     }
62280                 },
62281                 toolbar : {
62282                     xtype: 'Toolbar',
62283                     xns: Roo,
62284                     items : [
62285                         {
62286                             xtype: 'Button',
62287                             xns: Roo.Toolbar,
62288                             listeners : {
62289                                 click : function (_self, e)
62290                                 {
62291                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62292                                     sd.setMonth(sd.getMonth()-1);
62293                                     _this.monthField.setValue(sd.format('Y-m-d'));
62294                                     _this.grid.ds.load({});
62295                                 }
62296                             },
62297                             text : "Back"
62298                         },
62299                         {
62300                             xtype: 'Separator',
62301                             xns: Roo.Toolbar
62302                         },
62303                         {
62304                             xtype: 'MonthField',
62305                             xns: Roo.form,
62306                             listeners : {
62307                                 render : function (_self)
62308                                 {
62309                                     _this.monthField = _self;
62310                                    // _this.monthField.set  today
62311                                 },
62312                                 select : function (combo, date)
62313                                 {
62314                                     _this.grid.ds.load({});
62315                                 }
62316                             },
62317                             value : (function() { return new Date(); })()
62318                         },
62319                         {
62320                             xtype: 'Separator',
62321                             xns: Roo.Toolbar
62322                         },
62323                         {
62324                             xtype: 'TextItem',
62325                             xns: Roo.Toolbar,
62326                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62327                         },
62328                         {
62329                             xtype: 'Fill',
62330                             xns: Roo.Toolbar
62331                         },
62332                         {
62333                             xtype: 'Button',
62334                             xns: Roo.Toolbar,
62335                             listeners : {
62336                                 click : function (_self, e)
62337                                 {
62338                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62339                                     sd.setMonth(sd.getMonth()+1);
62340                                     _this.monthField.setValue(sd.format('Y-m-d'));
62341                                     _this.grid.ds.load({});
62342                                 }
62343                             },
62344                             text : "Next"
62345                         }
62346                     ]
62347                 },
62348                  
62349             }
62350         };
62351         
62352         *//*
62353  * Based on:
62354  * Ext JS Library 1.1.1
62355  * Copyright(c) 2006-2007, Ext JS, LLC.
62356  *
62357  * Originally Released Under LGPL - original licence link has changed is not relivant.
62358  *
62359  * Fork - LGPL
62360  * <script type="text/javascript">
62361  */
62362  
62363 /**
62364  * @class Roo.LoadMask
62365  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62366  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62367  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62368  * element's UpdateManager load indicator and will be destroyed after the initial load.
62369  * @constructor
62370  * Create a new LoadMask
62371  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62372  * @param {Object} config The config object
62373  */
62374 Roo.LoadMask = function(el, config){
62375     this.el = Roo.get(el);
62376     Roo.apply(this, config);
62377     if(this.store){
62378         this.store.on('beforeload', this.onBeforeLoad, this);
62379         this.store.on('load', this.onLoad, this);
62380         this.store.on('loadexception', this.onLoadException, this);
62381         this.removeMask = false;
62382     }else{
62383         var um = this.el.getUpdateManager();
62384         um.showLoadIndicator = false; // disable the default indicator
62385         um.on('beforeupdate', this.onBeforeLoad, this);
62386         um.on('update', this.onLoad, this);
62387         um.on('failure', this.onLoad, this);
62388         this.removeMask = true;
62389     }
62390 };
62391
62392 Roo.LoadMask.prototype = {
62393     /**
62394      * @cfg {Boolean} removeMask
62395      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62396      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62397      */
62398     removeMask : false,
62399     /**
62400      * @cfg {String} msg
62401      * The text to display in a centered loading message box (defaults to 'Loading...')
62402      */
62403     msg : 'Loading...',
62404     /**
62405      * @cfg {String} msgCls
62406      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62407      */
62408     msgCls : 'x-mask-loading',
62409
62410     /**
62411      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62412      * @type Boolean
62413      */
62414     disabled: false,
62415
62416     /**
62417      * Disables the mask to prevent it from being displayed
62418      */
62419     disable : function(){
62420        this.disabled = true;
62421     },
62422
62423     /**
62424      * Enables the mask so that it can be displayed
62425      */
62426     enable : function(){
62427         this.disabled = false;
62428     },
62429     
62430     onLoadException : function()
62431     {
62432         Roo.log(arguments);
62433         
62434         if (typeof(arguments[3]) != 'undefined') {
62435             Roo.MessageBox.alert("Error loading",arguments[3]);
62436         } 
62437         /*
62438         try {
62439             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62440                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62441             }   
62442         } catch(e) {
62443             
62444         }
62445         */
62446     
62447         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62448     },
62449     // private
62450     onLoad : function()
62451     {
62452         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62453     },
62454
62455     // private
62456     onBeforeLoad : function(){
62457         if(!this.disabled){
62458             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
62459         }
62460     },
62461
62462     // private
62463     destroy : function(){
62464         if(this.store){
62465             this.store.un('beforeload', this.onBeforeLoad, this);
62466             this.store.un('load', this.onLoad, this);
62467             this.store.un('loadexception', this.onLoadException, this);
62468         }else{
62469             var um = this.el.getUpdateManager();
62470             um.un('beforeupdate', this.onBeforeLoad, this);
62471             um.un('update', this.onLoad, this);
62472             um.un('failure', this.onLoad, this);
62473         }
62474     }
62475 };/*
62476  * Based on:
62477  * Ext JS Library 1.1.1
62478  * Copyright(c) 2006-2007, Ext JS, LLC.
62479  *
62480  * Originally Released Under LGPL - original licence link has changed is not relivant.
62481  *
62482  * Fork - LGPL
62483  * <script type="text/javascript">
62484  */
62485
62486
62487 /**
62488  * @class Roo.XTemplate
62489  * @extends Roo.Template
62490  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
62491 <pre><code>
62492 var t = new Roo.XTemplate(
62493         '&lt;select name="{name}"&gt;',
62494                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
62495         '&lt;/select&gt;'
62496 );
62497  
62498 // then append, applying the master template values
62499  </code></pre>
62500  *
62501  * Supported features:
62502  *
62503  *  Tags:
62504
62505 <pre><code>
62506       {a_variable} - output encoded.
62507       {a_variable.format:("Y-m-d")} - call a method on the variable
62508       {a_variable:raw} - unencoded output
62509       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
62510       {a_variable:this.method_on_template(...)} - call a method on the template object.
62511  
62512 </code></pre>
62513  *  The tpl tag:
62514 <pre><code>
62515         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
62516         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
62517         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
62518         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
62519   
62520         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
62521         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
62522 </code></pre>
62523  *      
62524  */
62525 Roo.XTemplate = function()
62526 {
62527     Roo.XTemplate.superclass.constructor.apply(this, arguments);
62528     if (this.html) {
62529         this.compile();
62530     }
62531 };
62532
62533
62534 Roo.extend(Roo.XTemplate, Roo.Template, {
62535
62536     /**
62537      * The various sub templates
62538      */
62539     tpls : false,
62540     /**
62541      *
62542      * basic tag replacing syntax
62543      * WORD:WORD()
62544      *
62545      * // you can fake an object call by doing this
62546      *  x.t:(test,tesT) 
62547      * 
62548      */
62549     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
62550
62551     /**
62552      * compile the template
62553      *
62554      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
62555      *
62556      */
62557     compile: function()
62558     {
62559         var s = this.html;
62560      
62561         s = ['<tpl>', s, '</tpl>'].join('');
62562     
62563         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
62564             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
62565             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
62566             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
62567             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
62568             m,
62569             id     = 0,
62570             tpls   = [];
62571     
62572         while(true == !!(m = s.match(re))){
62573             var forMatch   = m[0].match(nameRe),
62574                 ifMatch   = m[0].match(ifRe),
62575                 execMatch   = m[0].match(execRe),
62576                 namedMatch   = m[0].match(namedRe),
62577                 
62578                 exp  = null, 
62579                 fn   = null,
62580                 exec = null,
62581                 name = forMatch && forMatch[1] ? forMatch[1] : '';
62582                 
62583             if (ifMatch) {
62584                 // if - puts fn into test..
62585                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
62586                 if(exp){
62587                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
62588                 }
62589             }
62590             
62591             if (execMatch) {
62592                 // exec - calls a function... returns empty if true is  returned.
62593                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
62594                 if(exp){
62595                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
62596                 }
62597             }
62598             
62599             
62600             if (name) {
62601                 // for = 
62602                 switch(name){
62603                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
62604                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
62605                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
62606                 }
62607             }
62608             var uid = namedMatch ? namedMatch[1] : id;
62609             
62610             
62611             tpls.push({
62612                 id:     namedMatch ? namedMatch[1] : id,
62613                 target: name,
62614                 exec:   exec,
62615                 test:   fn,
62616                 body:   m[1] || ''
62617             });
62618             if (namedMatch) {
62619                 s = s.replace(m[0], '');
62620             } else { 
62621                 s = s.replace(m[0], '{xtpl'+ id + '}');
62622             }
62623             ++id;
62624         }
62625         this.tpls = [];
62626         for(var i = tpls.length-1; i >= 0; --i){
62627             this.compileTpl(tpls[i]);
62628             this.tpls[tpls[i].id] = tpls[i];
62629         }
62630         this.master = tpls[tpls.length-1];
62631         return this;
62632     },
62633     /**
62634      * same as applyTemplate, except it's done to one of the subTemplates
62635      * when using named templates, you can do:
62636      *
62637      * var str = pl.applySubTemplate('your-name', values);
62638      *
62639      * 
62640      * @param {Number} id of the template
62641      * @param {Object} values to apply to template
62642      * @param {Object} parent (normaly the instance of this object)
62643      */
62644     applySubTemplate : function(id, values, parent)
62645     {
62646         
62647         
62648         var t = this.tpls[id];
62649         
62650         
62651         try { 
62652             if(t.test && !t.test.call(this, values, parent)){
62653                 return '';
62654             }
62655         } catch(e) {
62656             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
62657             Roo.log(e.toString());
62658             Roo.log(t.test);
62659             return ''
62660         }
62661         try { 
62662             
62663             if(t.exec && t.exec.call(this, values, parent)){
62664                 return '';
62665             }
62666         } catch(e) {
62667             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
62668             Roo.log(e.toString());
62669             Roo.log(t.exec);
62670             return ''
62671         }
62672         try {
62673             var vs = t.target ? t.target.call(this, values, parent) : values;
62674             parent = t.target ? values : parent;
62675             if(t.target && vs instanceof Array){
62676                 var buf = [];
62677                 for(var i = 0, len = vs.length; i < len; i++){
62678                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
62679                 }
62680                 return buf.join('');
62681             }
62682             return t.compiled.call(this, vs, parent);
62683         } catch (e) {
62684             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
62685             Roo.log(e.toString());
62686             Roo.log(t.compiled);
62687             return '';
62688         }
62689     },
62690
62691     compileTpl : function(tpl)
62692     {
62693         var fm = Roo.util.Format;
62694         var useF = this.disableFormats !== true;
62695         var sep = Roo.isGecko ? "+" : ",";
62696         var undef = function(str) {
62697             Roo.log("Property not found :"  + str);
62698             return '';
62699         };
62700         
62701         var fn = function(m, name, format, args)
62702         {
62703             //Roo.log(arguments);
62704             args = args ? args.replace(/\\'/g,"'") : args;
62705             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
62706             if (typeof(format) == 'undefined') {
62707                 format= 'htmlEncode';
62708             }
62709             if (format == 'raw' ) {
62710                 format = false;
62711             }
62712             
62713             if(name.substr(0, 4) == 'xtpl'){
62714                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
62715             }
62716             
62717             // build an array of options to determine if value is undefined..
62718             
62719             // basically get 'xxxx.yyyy' then do
62720             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
62721             //    (function () { Roo.log("Property not found"); return ''; })() :
62722             //    ......
62723             
62724             var udef_ar = [];
62725             var lookfor = '';
62726             Roo.each(name.split('.'), function(st) {
62727                 lookfor += (lookfor.length ? '.': '') + st;
62728                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62729             });
62730             
62731             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62732             
62733             
62734             if(format && useF){
62735                 
62736                 args = args ? ',' + args : "";
62737                  
62738                 if(format.substr(0, 5) != "this."){
62739                     format = "fm." + format + '(';
62740                 }else{
62741                     format = 'this.call("'+ format.substr(5) + '", ';
62742                     args = ", values";
62743                 }
62744                 
62745                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62746             }
62747              
62748             if (args.length) {
62749                 // called with xxyx.yuu:(test,test)
62750                 // change to ()
62751                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62752             }
62753             // raw.. - :raw modifier..
62754             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62755             
62756         };
62757         var body;
62758         // branched to use + in gecko and [].join() in others
62759         if(Roo.isGecko){
62760             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62761                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62762                     "';};};";
62763         }else{
62764             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62765             body.push(tpl.body.replace(/(\r\n|\n)/g,
62766                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62767             body.push("'].join('');};};");
62768             body = body.join('');
62769         }
62770         
62771         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62772        
62773         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62774         eval(body);
62775         
62776         return this;
62777     },
62778
62779     applyTemplate : function(values){
62780         return this.master.compiled.call(this, values, {});
62781         //var s = this.subs;
62782     },
62783
62784     apply : function(){
62785         return this.applyTemplate.apply(this, arguments);
62786     }
62787
62788  });
62789
62790 Roo.XTemplate.from = function(el){
62791     el = Roo.getDom(el);
62792     return new Roo.XTemplate(el.value || el.innerHTML);
62793 };