fix menu item # triggering url change
[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             var record = new Record(values, id);
25861             record.json = n;
25862             records[i] = record;
25863         }
25864         return {
25865             raw : o,
25866             success : success,
25867             records : records,
25868             totalRecords : totalRecords
25869         };
25870     },
25871     // used when loading children.. @see loadDataFromChildren
25872     toLoadData: function(rec)
25873     {
25874         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25875         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25876         return { data : data, total : data.length };
25877         
25878     }
25879 });/*
25880  * Based on:
25881  * Ext JS Library 1.1.1
25882  * Copyright(c) 2006-2007, Ext JS, LLC.
25883  *
25884  * Originally Released Under LGPL - original licence link has changed is not relivant.
25885  *
25886  * Fork - LGPL
25887  * <script type="text/javascript">
25888  */
25889
25890 /**
25891  * @class Roo.data.XmlReader
25892  * @extends Roo.data.DataReader
25893  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25894  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25895  * <p>
25896  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25897  * header in the HTTP response must be set to "text/xml".</em>
25898  * <p>
25899  * Example code:
25900  * <pre><code>
25901 var RecordDef = Roo.data.Record.create([
25902    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25903    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25904 ]);
25905 var myReader = new Roo.data.XmlReader({
25906    totalRecords: "results", // The element which contains the total dataset size (optional)
25907    record: "row",           // The repeated element which contains row information
25908    id: "id"                 // The element within the row that provides an ID for the record (optional)
25909 }, RecordDef);
25910 </code></pre>
25911  * <p>
25912  * This would consume an XML file like this:
25913  * <pre><code>
25914 &lt;?xml?>
25915 &lt;dataset>
25916  &lt;results>2&lt;/results>
25917  &lt;row>
25918    &lt;id>1&lt;/id>
25919    &lt;name>Bill&lt;/name>
25920    &lt;occupation>Gardener&lt;/occupation>
25921  &lt;/row>
25922  &lt;row>
25923    &lt;id>2&lt;/id>
25924    &lt;name>Ben&lt;/name>
25925    &lt;occupation>Horticulturalist&lt;/occupation>
25926  &lt;/row>
25927 &lt;/dataset>
25928 </code></pre>
25929  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25930  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25931  * paged from the remote server.
25932  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25933  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25934  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25935  * a record identifier value.
25936  * @constructor
25937  * Create a new XmlReader
25938  * @param {Object} meta Metadata configuration options
25939  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25940  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25941  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25942  */
25943 Roo.data.XmlReader = function(meta, recordType){
25944     meta = meta || {};
25945     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25946 };
25947 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25948     
25949     readerType : 'Xml',
25950     
25951     /**
25952      * This method is only used by a DataProxy which has retrieved data from a remote server.
25953          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25954          * to contain a method called 'responseXML' that returns an XML document object.
25955      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25956      * a cache of Roo.data.Records.
25957      */
25958     read : function(response){
25959         var doc = response.responseXML;
25960         if(!doc) {
25961             throw {message: "XmlReader.read: XML Document not available"};
25962         }
25963         return this.readRecords(doc);
25964     },
25965
25966     /**
25967      * Create a data block containing Roo.data.Records from an XML document.
25968          * @param {Object} doc A parsed XML document.
25969      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25970      * a cache of Roo.data.Records.
25971      */
25972     readRecords : function(doc){
25973         /**
25974          * After any data loads/reads, the raw XML Document is available for further custom processing.
25975          * @type XMLDocument
25976          */
25977         this.xmlData = doc;
25978         var root = doc.documentElement || doc;
25979         var q = Roo.DomQuery;
25980         var recordType = this.recordType, fields = recordType.prototype.fields;
25981         var sid = this.meta.id;
25982         var totalRecords = 0, success = true;
25983         if(this.meta.totalRecords){
25984             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25985         }
25986         
25987         if(this.meta.success){
25988             var sv = q.selectValue(this.meta.success, root, true);
25989             success = sv !== false && sv !== 'false';
25990         }
25991         var records = [];
25992         var ns = q.select(this.meta.record, root);
25993         for(var i = 0, len = ns.length; i < len; i++) {
25994                 var n = ns[i];
25995                 var values = {};
25996                 var id = sid ? q.selectValue(sid, n) : undefined;
25997                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25998                     var f = fields.items[j];
25999                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26000                     v = f.convert(v);
26001                     values[f.name] = v;
26002                 }
26003                 var record = new recordType(values, id);
26004                 record.node = n;
26005                 records[records.length] = record;
26006             }
26007
26008             return {
26009                 success : success,
26010                 records : records,
26011                 totalRecords : totalRecords || records.length
26012             };
26013     }
26014 });/*
26015  * Based on:
26016  * Ext JS Library 1.1.1
26017  * Copyright(c) 2006-2007, Ext JS, LLC.
26018  *
26019  * Originally Released Under LGPL - original licence link has changed is not relivant.
26020  *
26021  * Fork - LGPL
26022  * <script type="text/javascript">
26023  */
26024
26025 /**
26026  * @class Roo.data.ArrayReader
26027  * @extends Roo.data.DataReader
26028  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26029  * Each element of that Array represents a row of data fields. The
26030  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26031  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26032  * <p>
26033  * Example code:.
26034  * <pre><code>
26035 var RecordDef = Roo.data.Record.create([
26036     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26037     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26038 ]);
26039 var myReader = new Roo.data.ArrayReader({
26040     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26041 }, RecordDef);
26042 </code></pre>
26043  * <p>
26044  * This would consume an Array like this:
26045  * <pre><code>
26046 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26047   </code></pre>
26048  
26049  * @constructor
26050  * Create a new JsonReader
26051  * @param {Object} meta Metadata configuration options.
26052  * @param {Object|Array} recordType Either an Array of field definition objects
26053  * 
26054  * @cfg {Array} fields Array of field definition objects
26055  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26056  * as specified to {@link Roo.data.Record#create},
26057  * or an {@link Roo.data.Record} object
26058  *
26059  * 
26060  * created using {@link Roo.data.Record#create}.
26061  */
26062 Roo.data.ArrayReader = function(meta, recordType)
26063 {    
26064     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26065 };
26066
26067 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26068     
26069       /**
26070      * Create a data block containing Roo.data.Records from an XML document.
26071      * @param {Object} o An Array of row objects which represents the dataset.
26072      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26073      * a cache of Roo.data.Records.
26074      */
26075     readRecords : function(o)
26076     {
26077         var sid = this.meta ? this.meta.id : null;
26078         var recordType = this.recordType, fields = recordType.prototype.fields;
26079         var records = [];
26080         var root = o;
26081         for(var i = 0; i < root.length; i++){
26082             var n = root[i];
26083             var values = {};
26084             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26085             for(var j = 0, jlen = fields.length; j < jlen; j++){
26086                 var f = fields.items[j];
26087                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26088                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26089                 v = f.convert(v);
26090                 values[f.name] = v;
26091             }
26092             var record = new recordType(values, id);
26093             record.json = n;
26094             records[records.length] = record;
26095         }
26096         return {
26097             records : records,
26098             totalRecords : records.length
26099         };
26100     },
26101     // used when loading children.. @see loadDataFromChildren
26102     toLoadData: function(rec)
26103     {
26104         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26105         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26106         
26107     }
26108     
26109     
26110 });/*
26111  * Based on:
26112  * Ext JS Library 1.1.1
26113  * Copyright(c) 2006-2007, Ext JS, LLC.
26114  *
26115  * Originally Released Under LGPL - original licence link has changed is not relivant.
26116  *
26117  * Fork - LGPL
26118  * <script type="text/javascript">
26119  */
26120
26121
26122 /**
26123  * @class Roo.data.Tree
26124  * @extends Roo.util.Observable
26125  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26126  * in the tree have most standard DOM functionality.
26127  * @constructor
26128  * @param {Node} root (optional) The root node
26129  */
26130 Roo.data.Tree = function(root){
26131    this.nodeHash = {};
26132    /**
26133     * The root node for this tree
26134     * @type Node
26135     */
26136    this.root = null;
26137    if(root){
26138        this.setRootNode(root);
26139    }
26140    this.addEvents({
26141        /**
26142         * @event append
26143         * Fires when a new child node is appended to a node in this tree.
26144         * @param {Tree} tree The owner tree
26145         * @param {Node} parent The parent node
26146         * @param {Node} node The newly appended node
26147         * @param {Number} index The index of the newly appended node
26148         */
26149        "append" : true,
26150        /**
26151         * @event remove
26152         * Fires when a child node is removed from a node in this tree.
26153         * @param {Tree} tree The owner tree
26154         * @param {Node} parent The parent node
26155         * @param {Node} node The child node removed
26156         */
26157        "remove" : true,
26158        /**
26159         * @event move
26160         * Fires when a node is moved to a new location in the tree
26161         * @param {Tree} tree The owner tree
26162         * @param {Node} node The node moved
26163         * @param {Node} oldParent The old parent of this node
26164         * @param {Node} newParent The new parent of this node
26165         * @param {Number} index The index it was moved to
26166         */
26167        "move" : true,
26168        /**
26169         * @event insert
26170         * Fires when a new child node is inserted in a node in this tree.
26171         * @param {Tree} tree The owner tree
26172         * @param {Node} parent The parent node
26173         * @param {Node} node The child node inserted
26174         * @param {Node} refNode The child node the node was inserted before
26175         */
26176        "insert" : true,
26177        /**
26178         * @event beforeappend
26179         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26180         * @param {Tree} tree The owner tree
26181         * @param {Node} parent The parent node
26182         * @param {Node} node The child node to be appended
26183         */
26184        "beforeappend" : true,
26185        /**
26186         * @event beforeremove
26187         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26188         * @param {Tree} tree The owner tree
26189         * @param {Node} parent The parent node
26190         * @param {Node} node The child node to be removed
26191         */
26192        "beforeremove" : true,
26193        /**
26194         * @event beforemove
26195         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26196         * @param {Tree} tree The owner tree
26197         * @param {Node} node The node being moved
26198         * @param {Node} oldParent The parent of the node
26199         * @param {Node} newParent The new parent the node is moving to
26200         * @param {Number} index The index it is being moved to
26201         */
26202        "beforemove" : true,
26203        /**
26204         * @event beforeinsert
26205         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26206         * @param {Tree} tree The owner tree
26207         * @param {Node} parent The parent node
26208         * @param {Node} node The child node to be inserted
26209         * @param {Node} refNode The child node the node is being inserted before
26210         */
26211        "beforeinsert" : true
26212    });
26213
26214     Roo.data.Tree.superclass.constructor.call(this);
26215 };
26216
26217 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26218     pathSeparator: "/",
26219
26220     proxyNodeEvent : function(){
26221         return this.fireEvent.apply(this, arguments);
26222     },
26223
26224     /**
26225      * Returns the root node for this tree.
26226      * @return {Node}
26227      */
26228     getRootNode : function(){
26229         return this.root;
26230     },
26231
26232     /**
26233      * Sets the root node for this tree.
26234      * @param {Node} node
26235      * @return {Node}
26236      */
26237     setRootNode : function(node){
26238         this.root = node;
26239         node.ownerTree = this;
26240         node.isRoot = true;
26241         this.registerNode(node);
26242         return node;
26243     },
26244
26245     /**
26246      * Gets a node in this tree by its id.
26247      * @param {String} id
26248      * @return {Node}
26249      */
26250     getNodeById : function(id){
26251         return this.nodeHash[id];
26252     },
26253
26254     registerNode : function(node){
26255         this.nodeHash[node.id] = node;
26256     },
26257
26258     unregisterNode : function(node){
26259         delete this.nodeHash[node.id];
26260     },
26261
26262     toString : function(){
26263         return "[Tree"+(this.id?" "+this.id:"")+"]";
26264     }
26265 });
26266
26267 /**
26268  * @class Roo.data.Node
26269  * @extends Roo.util.Observable
26270  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26271  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26272  * @constructor
26273  * @param {Object} attributes The attributes/config for the node
26274  */
26275 Roo.data.Node = function(attributes){
26276     /**
26277      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26278      * @type {Object}
26279      */
26280     this.attributes = attributes || {};
26281     this.leaf = this.attributes.leaf;
26282     /**
26283      * The node id. @type String
26284      */
26285     this.id = this.attributes.id;
26286     if(!this.id){
26287         this.id = Roo.id(null, "ynode-");
26288         this.attributes.id = this.id;
26289     }
26290      
26291     
26292     /**
26293      * All child nodes of this node. @type Array
26294      */
26295     this.childNodes = [];
26296     if(!this.childNodes.indexOf){ // indexOf is a must
26297         this.childNodes.indexOf = function(o){
26298             for(var i = 0, len = this.length; i < len; i++){
26299                 if(this[i] == o) {
26300                     return i;
26301                 }
26302             }
26303             return -1;
26304         };
26305     }
26306     /**
26307      * The parent node for this node. @type Node
26308      */
26309     this.parentNode = null;
26310     /**
26311      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26312      */
26313     this.firstChild = null;
26314     /**
26315      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26316      */
26317     this.lastChild = null;
26318     /**
26319      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26320      */
26321     this.previousSibling = null;
26322     /**
26323      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26324      */
26325     this.nextSibling = null;
26326
26327     this.addEvents({
26328        /**
26329         * @event append
26330         * Fires when a new child node is appended
26331         * @param {Tree} tree The owner tree
26332         * @param {Node} this This node
26333         * @param {Node} node The newly appended node
26334         * @param {Number} index The index of the newly appended node
26335         */
26336        "append" : true,
26337        /**
26338         * @event remove
26339         * Fires when a child node is removed
26340         * @param {Tree} tree The owner tree
26341         * @param {Node} this This node
26342         * @param {Node} node The removed node
26343         */
26344        "remove" : true,
26345        /**
26346         * @event move
26347         * Fires when this node is moved to a new location in the tree
26348         * @param {Tree} tree The owner tree
26349         * @param {Node} this This node
26350         * @param {Node} oldParent The old parent of this node
26351         * @param {Node} newParent The new parent of this node
26352         * @param {Number} index The index it was moved to
26353         */
26354        "move" : true,
26355        /**
26356         * @event insert
26357         * Fires when a new child node is inserted.
26358         * @param {Tree} tree The owner tree
26359         * @param {Node} this This node
26360         * @param {Node} node The child node inserted
26361         * @param {Node} refNode The child node the node was inserted before
26362         */
26363        "insert" : true,
26364        /**
26365         * @event beforeappend
26366         * Fires before a new child is appended, return false to cancel the append.
26367         * @param {Tree} tree The owner tree
26368         * @param {Node} this This node
26369         * @param {Node} node The child node to be appended
26370         */
26371        "beforeappend" : true,
26372        /**
26373         * @event beforeremove
26374         * Fires before a child is removed, return false to cancel the remove.
26375         * @param {Tree} tree The owner tree
26376         * @param {Node} this This node
26377         * @param {Node} node The child node to be removed
26378         */
26379        "beforeremove" : true,
26380        /**
26381         * @event beforemove
26382         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26383         * @param {Tree} tree The owner tree
26384         * @param {Node} this This node
26385         * @param {Node} oldParent The parent of this node
26386         * @param {Node} newParent The new parent this node is moving to
26387         * @param {Number} index The index it is being moved to
26388         */
26389        "beforemove" : true,
26390        /**
26391         * @event beforeinsert
26392         * Fires before a new child is inserted, return false to cancel the insert.
26393         * @param {Tree} tree The owner tree
26394         * @param {Node} this This node
26395         * @param {Node} node The child node to be inserted
26396         * @param {Node} refNode The child node the node is being inserted before
26397         */
26398        "beforeinsert" : true
26399    });
26400     this.listeners = this.attributes.listeners;
26401     Roo.data.Node.superclass.constructor.call(this);
26402 };
26403
26404 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26405     fireEvent : function(evtName){
26406         // first do standard event for this node
26407         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26408             return false;
26409         }
26410         // then bubble it up to the tree if the event wasn't cancelled
26411         var ot = this.getOwnerTree();
26412         if(ot){
26413             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26414                 return false;
26415             }
26416         }
26417         return true;
26418     },
26419
26420     /**
26421      * Returns true if this node is a leaf
26422      * @return {Boolean}
26423      */
26424     isLeaf : function(){
26425         return this.leaf === true;
26426     },
26427
26428     // private
26429     setFirstChild : function(node){
26430         this.firstChild = node;
26431     },
26432
26433     //private
26434     setLastChild : function(node){
26435         this.lastChild = node;
26436     },
26437
26438
26439     /**
26440      * Returns true if this node is the last child of its parent
26441      * @return {Boolean}
26442      */
26443     isLast : function(){
26444        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26445     },
26446
26447     /**
26448      * Returns true if this node is the first child of its parent
26449      * @return {Boolean}
26450      */
26451     isFirst : function(){
26452        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26453     },
26454
26455     hasChildNodes : function(){
26456         return !this.isLeaf() && this.childNodes.length > 0;
26457     },
26458
26459     /**
26460      * Insert node(s) as the last child node of this node.
26461      * @param {Node/Array} node The node or Array of nodes to append
26462      * @return {Node} The appended node if single append, or null if an array was passed
26463      */
26464     appendChild : function(node){
26465         var multi = false;
26466         if(node instanceof Array){
26467             multi = node;
26468         }else if(arguments.length > 1){
26469             multi = arguments;
26470         }
26471         
26472         // if passed an array or multiple args do them one by one
26473         if(multi){
26474             for(var i = 0, len = multi.length; i < len; i++) {
26475                 this.appendChild(multi[i]);
26476             }
26477         }else{
26478             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26479                 return false;
26480             }
26481             var index = this.childNodes.length;
26482             var oldParent = node.parentNode;
26483             // it's a move, make sure we move it cleanly
26484             if(oldParent){
26485                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26486                     return false;
26487                 }
26488                 oldParent.removeChild(node);
26489             }
26490             
26491             index = this.childNodes.length;
26492             if(index == 0){
26493                 this.setFirstChild(node);
26494             }
26495             this.childNodes.push(node);
26496             node.parentNode = this;
26497             var ps = this.childNodes[index-1];
26498             if(ps){
26499                 node.previousSibling = ps;
26500                 ps.nextSibling = node;
26501             }else{
26502                 node.previousSibling = null;
26503             }
26504             node.nextSibling = null;
26505             this.setLastChild(node);
26506             node.setOwnerTree(this.getOwnerTree());
26507             this.fireEvent("append", this.ownerTree, this, node, index);
26508             if(this.ownerTree) {
26509                 this.ownerTree.fireEvent("appendnode", this, node, index);
26510             }
26511             if(oldParent){
26512                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26513             }
26514             return node;
26515         }
26516     },
26517
26518     /**
26519      * Removes a child node from this node.
26520      * @param {Node} node The node to remove
26521      * @return {Node} The removed node
26522      */
26523     removeChild : function(node){
26524         var index = this.childNodes.indexOf(node);
26525         if(index == -1){
26526             return false;
26527         }
26528         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26529             return false;
26530         }
26531
26532         // remove it from childNodes collection
26533         this.childNodes.splice(index, 1);
26534
26535         // update siblings
26536         if(node.previousSibling){
26537             node.previousSibling.nextSibling = node.nextSibling;
26538         }
26539         if(node.nextSibling){
26540             node.nextSibling.previousSibling = node.previousSibling;
26541         }
26542
26543         // update child refs
26544         if(this.firstChild == node){
26545             this.setFirstChild(node.nextSibling);
26546         }
26547         if(this.lastChild == node){
26548             this.setLastChild(node.previousSibling);
26549         }
26550
26551         node.setOwnerTree(null);
26552         // clear any references from the node
26553         node.parentNode = null;
26554         node.previousSibling = null;
26555         node.nextSibling = null;
26556         this.fireEvent("remove", this.ownerTree, this, node);
26557         return node;
26558     },
26559
26560     /**
26561      * Inserts the first node before the second node in this nodes childNodes collection.
26562      * @param {Node} node The node to insert
26563      * @param {Node} refNode The node to insert before (if null the node is appended)
26564      * @return {Node} The inserted node
26565      */
26566     insertBefore : function(node, refNode){
26567         if(!refNode){ // like standard Dom, refNode can be null for append
26568             return this.appendChild(node);
26569         }
26570         // nothing to do
26571         if(node == refNode){
26572             return false;
26573         }
26574
26575         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26576             return false;
26577         }
26578         var index = this.childNodes.indexOf(refNode);
26579         var oldParent = node.parentNode;
26580         var refIndex = index;
26581
26582         // when moving internally, indexes will change after remove
26583         if(oldParent == this && this.childNodes.indexOf(node) < index){
26584             refIndex--;
26585         }
26586
26587         // it's a move, make sure we move it cleanly
26588         if(oldParent){
26589             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26590                 return false;
26591             }
26592             oldParent.removeChild(node);
26593         }
26594         if(refIndex == 0){
26595             this.setFirstChild(node);
26596         }
26597         this.childNodes.splice(refIndex, 0, node);
26598         node.parentNode = this;
26599         var ps = this.childNodes[refIndex-1];
26600         if(ps){
26601             node.previousSibling = ps;
26602             ps.nextSibling = node;
26603         }else{
26604             node.previousSibling = null;
26605         }
26606         node.nextSibling = refNode;
26607         refNode.previousSibling = node;
26608         node.setOwnerTree(this.getOwnerTree());
26609         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26610         if(oldParent){
26611             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26612         }
26613         return node;
26614     },
26615
26616     /**
26617      * Returns the child node at the specified index.
26618      * @param {Number} index
26619      * @return {Node}
26620      */
26621     item : function(index){
26622         return this.childNodes[index];
26623     },
26624
26625     /**
26626      * Replaces one child node in this node with another.
26627      * @param {Node} newChild The replacement node
26628      * @param {Node} oldChild The node to replace
26629      * @return {Node} The replaced node
26630      */
26631     replaceChild : function(newChild, oldChild){
26632         this.insertBefore(newChild, oldChild);
26633         this.removeChild(oldChild);
26634         return oldChild;
26635     },
26636
26637     /**
26638      * Returns the index of a child node
26639      * @param {Node} node
26640      * @return {Number} The index of the node or -1 if it was not found
26641      */
26642     indexOf : function(child){
26643         return this.childNodes.indexOf(child);
26644     },
26645
26646     /**
26647      * Returns the tree this node is in.
26648      * @return {Tree}
26649      */
26650     getOwnerTree : function(){
26651         // if it doesn't have one, look for one
26652         if(!this.ownerTree){
26653             var p = this;
26654             while(p){
26655                 if(p.ownerTree){
26656                     this.ownerTree = p.ownerTree;
26657                     break;
26658                 }
26659                 p = p.parentNode;
26660             }
26661         }
26662         return this.ownerTree;
26663     },
26664
26665     /**
26666      * Returns depth of this node (the root node has a depth of 0)
26667      * @return {Number}
26668      */
26669     getDepth : function(){
26670         var depth = 0;
26671         var p = this;
26672         while(p.parentNode){
26673             ++depth;
26674             p = p.parentNode;
26675         }
26676         return depth;
26677     },
26678
26679     // private
26680     setOwnerTree : function(tree){
26681         // if it's move, we need to update everyone
26682         if(tree != this.ownerTree){
26683             if(this.ownerTree){
26684                 this.ownerTree.unregisterNode(this);
26685             }
26686             this.ownerTree = tree;
26687             var cs = this.childNodes;
26688             for(var i = 0, len = cs.length; i < len; i++) {
26689                 cs[i].setOwnerTree(tree);
26690             }
26691             if(tree){
26692                 tree.registerNode(this);
26693             }
26694         }
26695     },
26696
26697     /**
26698      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26699      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26700      * @return {String} The path
26701      */
26702     getPath : function(attr){
26703         attr = attr || "id";
26704         var p = this.parentNode;
26705         var b = [this.attributes[attr]];
26706         while(p){
26707             b.unshift(p.attributes[attr]);
26708             p = p.parentNode;
26709         }
26710         var sep = this.getOwnerTree().pathSeparator;
26711         return sep + b.join(sep);
26712     },
26713
26714     /**
26715      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26716      * function call will be the scope provided or the current node. The arguments to the function
26717      * will be the args provided or the current node. If the function returns false at any point,
26718      * the bubble is stopped.
26719      * @param {Function} fn The function to call
26720      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26721      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26722      */
26723     bubble : function(fn, scope, args){
26724         var p = this;
26725         while(p){
26726             if(fn.call(scope || p, args || p) === false){
26727                 break;
26728             }
26729             p = p.parentNode;
26730         }
26731     },
26732
26733     /**
26734      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26735      * function call will be the scope provided or the current node. The arguments to the function
26736      * will be the args provided or the current node. If the function returns false at any point,
26737      * the cascade is stopped on that branch.
26738      * @param {Function} fn The function to call
26739      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26740      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26741      */
26742     cascade : function(fn, scope, args){
26743         if(fn.call(scope || this, args || this) !== false){
26744             var cs = this.childNodes;
26745             for(var i = 0, len = cs.length; i < len; i++) {
26746                 cs[i].cascade(fn, scope, args);
26747             }
26748         }
26749     },
26750
26751     /**
26752      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26753      * function call will be the scope provided or the current node. The arguments to the function
26754      * will be the args provided or the current node. If the function returns false at any point,
26755      * the iteration stops.
26756      * @param {Function} fn The function to call
26757      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26758      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26759      */
26760     eachChild : function(fn, scope, args){
26761         var cs = this.childNodes;
26762         for(var i = 0, len = cs.length; i < len; i++) {
26763                 if(fn.call(scope || this, args || cs[i]) === false){
26764                     break;
26765                 }
26766         }
26767     },
26768
26769     /**
26770      * Finds the first child that has the attribute with the specified value.
26771      * @param {String} attribute The attribute name
26772      * @param {Mixed} value The value to search for
26773      * @return {Node} The found child or null if none was found
26774      */
26775     findChild : function(attribute, value){
26776         var cs = this.childNodes;
26777         for(var i = 0, len = cs.length; i < len; i++) {
26778                 if(cs[i].attributes[attribute] == value){
26779                     return cs[i];
26780                 }
26781         }
26782         return null;
26783     },
26784
26785     /**
26786      * Finds the first child by a custom function. The child matches if the function passed
26787      * returns true.
26788      * @param {Function} fn
26789      * @param {Object} scope (optional)
26790      * @return {Node} The found child or null if none was found
26791      */
26792     findChildBy : function(fn, scope){
26793         var cs = this.childNodes;
26794         for(var i = 0, len = cs.length; i < len; i++) {
26795                 if(fn.call(scope||cs[i], cs[i]) === true){
26796                     return cs[i];
26797                 }
26798         }
26799         return null;
26800     },
26801
26802     /**
26803      * Sorts this nodes children using the supplied sort function
26804      * @param {Function} fn
26805      * @param {Object} scope (optional)
26806      */
26807     sort : function(fn, scope){
26808         var cs = this.childNodes;
26809         var len = cs.length;
26810         if(len > 0){
26811             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26812             cs.sort(sortFn);
26813             for(var i = 0; i < len; i++){
26814                 var n = cs[i];
26815                 n.previousSibling = cs[i-1];
26816                 n.nextSibling = cs[i+1];
26817                 if(i == 0){
26818                     this.setFirstChild(n);
26819                 }
26820                 if(i == len-1){
26821                     this.setLastChild(n);
26822                 }
26823             }
26824         }
26825     },
26826
26827     /**
26828      * Returns true if this node is an ancestor (at any point) of the passed node.
26829      * @param {Node} node
26830      * @return {Boolean}
26831      */
26832     contains : function(node){
26833         return node.isAncestor(this);
26834     },
26835
26836     /**
26837      * Returns true if the passed node is an ancestor (at any point) of this node.
26838      * @param {Node} node
26839      * @return {Boolean}
26840      */
26841     isAncestor : function(node){
26842         var p = this.parentNode;
26843         while(p){
26844             if(p == node){
26845                 return true;
26846             }
26847             p = p.parentNode;
26848         }
26849         return false;
26850     },
26851
26852     toString : function(){
26853         return "[Node"+(this.id?" "+this.id:"")+"]";
26854     }
26855 });/*
26856  * Based on:
26857  * Ext JS Library 1.1.1
26858  * Copyright(c) 2006-2007, Ext JS, LLC.
26859  *
26860  * Originally Released Under LGPL - original licence link has changed is not relivant.
26861  *
26862  * Fork - LGPL
26863  * <script type="text/javascript">
26864  */
26865
26866
26867 /**
26868  * @class Roo.Shadow
26869  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26870  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26871  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26872  * @constructor
26873  * Create a new Shadow
26874  * @param {Object} config The config object
26875  */
26876 Roo.Shadow = function(config){
26877     Roo.apply(this, config);
26878     if(typeof this.mode != "string"){
26879         this.mode = this.defaultMode;
26880     }
26881     var o = this.offset, a = {h: 0};
26882     var rad = Math.floor(this.offset/2);
26883     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26884         case "drop":
26885             a.w = 0;
26886             a.l = a.t = o;
26887             a.t -= 1;
26888             if(Roo.isIE){
26889                 a.l -= this.offset + rad;
26890                 a.t -= this.offset + rad;
26891                 a.w -= rad;
26892                 a.h -= rad;
26893                 a.t += 1;
26894             }
26895         break;
26896         case "sides":
26897             a.w = (o*2);
26898             a.l = -o;
26899             a.t = o-1;
26900             if(Roo.isIE){
26901                 a.l -= (this.offset - rad);
26902                 a.t -= this.offset + rad;
26903                 a.l += 1;
26904                 a.w -= (this.offset - rad)*2;
26905                 a.w -= rad + 1;
26906                 a.h -= 1;
26907             }
26908         break;
26909         case "frame":
26910             a.w = a.h = (o*2);
26911             a.l = a.t = -o;
26912             a.t += 1;
26913             a.h -= 2;
26914             if(Roo.isIE){
26915                 a.l -= (this.offset - rad);
26916                 a.t -= (this.offset - rad);
26917                 a.l += 1;
26918                 a.w -= (this.offset + rad + 1);
26919                 a.h -= (this.offset + rad);
26920                 a.h += 1;
26921             }
26922         break;
26923     };
26924
26925     this.adjusts = a;
26926 };
26927
26928 Roo.Shadow.prototype = {
26929     /**
26930      * @cfg {String} mode
26931      * The shadow display mode.  Supports the following options:<br />
26932      * sides: Shadow displays on both sides and bottom only<br />
26933      * frame: Shadow displays equally on all four sides<br />
26934      * drop: Traditional bottom-right drop shadow (default)
26935      */
26936     mode: false,
26937     /**
26938      * @cfg {String} offset
26939      * The number of pixels to offset the shadow from the element (defaults to 4)
26940      */
26941     offset: 4,
26942
26943     // private
26944     defaultMode: "drop",
26945
26946     /**
26947      * Displays the shadow under the target element
26948      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26949      */
26950     show : function(target){
26951         target = Roo.get(target);
26952         if(!this.el){
26953             this.el = Roo.Shadow.Pool.pull();
26954             if(this.el.dom.nextSibling != target.dom){
26955                 this.el.insertBefore(target);
26956             }
26957         }
26958         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26959         if(Roo.isIE){
26960             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26961         }
26962         this.realign(
26963             target.getLeft(true),
26964             target.getTop(true),
26965             target.getWidth(),
26966             target.getHeight()
26967         );
26968         this.el.dom.style.display = "block";
26969     },
26970
26971     /**
26972      * Returns true if the shadow is visible, else false
26973      */
26974     isVisible : function(){
26975         return this.el ? true : false;  
26976     },
26977
26978     /**
26979      * Direct alignment when values are already available. Show must be called at least once before
26980      * calling this method to ensure it is initialized.
26981      * @param {Number} left The target element left position
26982      * @param {Number} top The target element top position
26983      * @param {Number} width The target element width
26984      * @param {Number} height The target element height
26985      */
26986     realign : function(l, t, w, h){
26987         if(!this.el){
26988             return;
26989         }
26990         var a = this.adjusts, d = this.el.dom, s = d.style;
26991         var iea = 0;
26992         s.left = (l+a.l)+"px";
26993         s.top = (t+a.t)+"px";
26994         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26995  
26996         if(s.width != sws || s.height != shs){
26997             s.width = sws;
26998             s.height = shs;
26999             if(!Roo.isIE){
27000                 var cn = d.childNodes;
27001                 var sww = Math.max(0, (sw-12))+"px";
27002                 cn[0].childNodes[1].style.width = sww;
27003                 cn[1].childNodes[1].style.width = sww;
27004                 cn[2].childNodes[1].style.width = sww;
27005                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27006             }
27007         }
27008     },
27009
27010     /**
27011      * Hides this shadow
27012      */
27013     hide : function(){
27014         if(this.el){
27015             this.el.dom.style.display = "none";
27016             Roo.Shadow.Pool.push(this.el);
27017             delete this.el;
27018         }
27019     },
27020
27021     /**
27022      * Adjust the z-index of this shadow
27023      * @param {Number} zindex The new z-index
27024      */
27025     setZIndex : function(z){
27026         this.zIndex = z;
27027         if(this.el){
27028             this.el.setStyle("z-index", z);
27029         }
27030     }
27031 };
27032
27033 // Private utility class that manages the internal Shadow cache
27034 Roo.Shadow.Pool = function(){
27035     var p = [];
27036     var markup = Roo.isIE ?
27037                  '<div class="x-ie-shadow"></div>' :
27038                  '<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>';
27039     return {
27040         pull : function(){
27041             var sh = p.shift();
27042             if(!sh){
27043                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27044                 sh.autoBoxAdjust = false;
27045             }
27046             return sh;
27047         },
27048
27049         push : function(sh){
27050             p.push(sh);
27051         }
27052     };
27053 }();/*
27054  * Based on:
27055  * Ext JS Library 1.1.1
27056  * Copyright(c) 2006-2007, Ext JS, LLC.
27057  *
27058  * Originally Released Under LGPL - original licence link has changed is not relivant.
27059  *
27060  * Fork - LGPL
27061  * <script type="text/javascript">
27062  */
27063
27064
27065 /**
27066  * @class Roo.SplitBar
27067  * @extends Roo.util.Observable
27068  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27069  * <br><br>
27070  * Usage:
27071  * <pre><code>
27072 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27073                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27074 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27075 split.minSize = 100;
27076 split.maxSize = 600;
27077 split.animate = true;
27078 split.on('moved', splitterMoved);
27079 </code></pre>
27080  * @constructor
27081  * Create a new SplitBar
27082  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27083  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27084  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27085  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27086                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27087                         position of the SplitBar).
27088  */
27089 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27090     
27091     /** @private */
27092     this.el = Roo.get(dragElement, true);
27093     this.el.dom.unselectable = "on";
27094     /** @private */
27095     this.resizingEl = Roo.get(resizingElement, true);
27096
27097     /**
27098      * @private
27099      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27100      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27101      * @type Number
27102      */
27103     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27104     
27105     /**
27106      * The minimum size of the resizing element. (Defaults to 0)
27107      * @type Number
27108      */
27109     this.minSize = 0;
27110     
27111     /**
27112      * The maximum size of the resizing element. (Defaults to 2000)
27113      * @type Number
27114      */
27115     this.maxSize = 2000;
27116     
27117     /**
27118      * Whether to animate the transition to the new size
27119      * @type Boolean
27120      */
27121     this.animate = false;
27122     
27123     /**
27124      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27125      * @type Boolean
27126      */
27127     this.useShim = false;
27128     
27129     /** @private */
27130     this.shim = null;
27131     
27132     if(!existingProxy){
27133         /** @private */
27134         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27135     }else{
27136         this.proxy = Roo.get(existingProxy).dom;
27137     }
27138     /** @private */
27139     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27140     
27141     /** @private */
27142     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27143     
27144     /** @private */
27145     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27146     
27147     /** @private */
27148     this.dragSpecs = {};
27149     
27150     /**
27151      * @private The adapter to use to positon and resize elements
27152      */
27153     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27154     this.adapter.init(this);
27155     
27156     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27157         /** @private */
27158         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27159         this.el.addClass("x-splitbar-h");
27160     }else{
27161         /** @private */
27162         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27163         this.el.addClass("x-splitbar-v");
27164     }
27165     
27166     this.addEvents({
27167         /**
27168          * @event resize
27169          * Fires when the splitter is moved (alias for {@link #event-moved})
27170          * @param {Roo.SplitBar} this
27171          * @param {Number} newSize the new width or height
27172          */
27173         "resize" : true,
27174         /**
27175          * @event moved
27176          * Fires when the splitter is moved
27177          * @param {Roo.SplitBar} this
27178          * @param {Number} newSize the new width or height
27179          */
27180         "moved" : true,
27181         /**
27182          * @event beforeresize
27183          * Fires before the splitter is dragged
27184          * @param {Roo.SplitBar} this
27185          */
27186         "beforeresize" : true,
27187
27188         "beforeapply" : true
27189     });
27190
27191     Roo.util.Observable.call(this);
27192 };
27193
27194 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27195     onStartProxyDrag : function(x, y){
27196         this.fireEvent("beforeresize", this);
27197         if(!this.overlay){
27198             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27199             o.unselectable();
27200             o.enableDisplayMode("block");
27201             // all splitbars share the same overlay
27202             Roo.SplitBar.prototype.overlay = o;
27203         }
27204         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27205         this.overlay.show();
27206         Roo.get(this.proxy).setDisplayed("block");
27207         var size = this.adapter.getElementSize(this);
27208         this.activeMinSize = this.getMinimumSize();;
27209         this.activeMaxSize = this.getMaximumSize();;
27210         var c1 = size - this.activeMinSize;
27211         var c2 = Math.max(this.activeMaxSize - size, 0);
27212         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27213             this.dd.resetConstraints();
27214             this.dd.setXConstraint(
27215                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27216                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27217             );
27218             this.dd.setYConstraint(0, 0);
27219         }else{
27220             this.dd.resetConstraints();
27221             this.dd.setXConstraint(0, 0);
27222             this.dd.setYConstraint(
27223                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27224                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27225             );
27226          }
27227         this.dragSpecs.startSize = size;
27228         this.dragSpecs.startPoint = [x, y];
27229         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27230     },
27231     
27232     /** 
27233      * @private Called after the drag operation by the DDProxy
27234      */
27235     onEndProxyDrag : function(e){
27236         Roo.get(this.proxy).setDisplayed(false);
27237         var endPoint = Roo.lib.Event.getXY(e);
27238         if(this.overlay){
27239             this.overlay.hide();
27240         }
27241         var newSize;
27242         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27243             newSize = this.dragSpecs.startSize + 
27244                 (this.placement == Roo.SplitBar.LEFT ?
27245                     endPoint[0] - this.dragSpecs.startPoint[0] :
27246                     this.dragSpecs.startPoint[0] - endPoint[0]
27247                 );
27248         }else{
27249             newSize = this.dragSpecs.startSize + 
27250                 (this.placement == Roo.SplitBar.TOP ?
27251                     endPoint[1] - this.dragSpecs.startPoint[1] :
27252                     this.dragSpecs.startPoint[1] - endPoint[1]
27253                 );
27254         }
27255         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27256         if(newSize != this.dragSpecs.startSize){
27257             if(this.fireEvent('beforeapply', this, newSize) !== false){
27258                 this.adapter.setElementSize(this, newSize);
27259                 this.fireEvent("moved", this, newSize);
27260                 this.fireEvent("resize", this, newSize);
27261             }
27262         }
27263     },
27264     
27265     /**
27266      * Get the adapter this SplitBar uses
27267      * @return The adapter object
27268      */
27269     getAdapter : function(){
27270         return this.adapter;
27271     },
27272     
27273     /**
27274      * Set the adapter this SplitBar uses
27275      * @param {Object} adapter A SplitBar adapter object
27276      */
27277     setAdapter : function(adapter){
27278         this.adapter = adapter;
27279         this.adapter.init(this);
27280     },
27281     
27282     /**
27283      * Gets the minimum size for the resizing element
27284      * @return {Number} The minimum size
27285      */
27286     getMinimumSize : function(){
27287         return this.minSize;
27288     },
27289     
27290     /**
27291      * Sets the minimum size for the resizing element
27292      * @param {Number} minSize The minimum size
27293      */
27294     setMinimumSize : function(minSize){
27295         this.minSize = minSize;
27296     },
27297     
27298     /**
27299      * Gets the maximum size for the resizing element
27300      * @return {Number} The maximum size
27301      */
27302     getMaximumSize : function(){
27303         return this.maxSize;
27304     },
27305     
27306     /**
27307      * Sets the maximum size for the resizing element
27308      * @param {Number} maxSize The maximum size
27309      */
27310     setMaximumSize : function(maxSize){
27311         this.maxSize = maxSize;
27312     },
27313     
27314     /**
27315      * Sets the initialize size for the resizing element
27316      * @param {Number} size The initial size
27317      */
27318     setCurrentSize : function(size){
27319         var oldAnimate = this.animate;
27320         this.animate = false;
27321         this.adapter.setElementSize(this, size);
27322         this.animate = oldAnimate;
27323     },
27324     
27325     /**
27326      * Destroy this splitbar. 
27327      * @param {Boolean} removeEl True to remove the element
27328      */
27329     destroy : function(removeEl){
27330         if(this.shim){
27331             this.shim.remove();
27332         }
27333         this.dd.unreg();
27334         this.proxy.parentNode.removeChild(this.proxy);
27335         if(removeEl){
27336             this.el.remove();
27337         }
27338     }
27339 });
27340
27341 /**
27342  * @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.
27343  */
27344 Roo.SplitBar.createProxy = function(dir){
27345     var proxy = new Roo.Element(document.createElement("div"));
27346     proxy.unselectable();
27347     var cls = 'x-splitbar-proxy';
27348     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27349     document.body.appendChild(proxy.dom);
27350     return proxy.dom;
27351 };
27352
27353 /** 
27354  * @class Roo.SplitBar.BasicLayoutAdapter
27355  * Default Adapter. It assumes the splitter and resizing element are not positioned
27356  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27357  */
27358 Roo.SplitBar.BasicLayoutAdapter = function(){
27359 };
27360
27361 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27362     // do nothing for now
27363     init : function(s){
27364     
27365     },
27366     /**
27367      * Called before drag operations to get the current size of the resizing element. 
27368      * @param {Roo.SplitBar} s The SplitBar using this adapter
27369      */
27370      getElementSize : function(s){
27371         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27372             return s.resizingEl.getWidth();
27373         }else{
27374             return s.resizingEl.getHeight();
27375         }
27376     },
27377     
27378     /**
27379      * Called after drag operations to set the size of the resizing element.
27380      * @param {Roo.SplitBar} s The SplitBar using this adapter
27381      * @param {Number} newSize The new size to set
27382      * @param {Function} onComplete A function to be invoked when resizing is complete
27383      */
27384     setElementSize : function(s, newSize, onComplete){
27385         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27386             if(!s.animate){
27387                 s.resizingEl.setWidth(newSize);
27388                 if(onComplete){
27389                     onComplete(s, newSize);
27390                 }
27391             }else{
27392                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27393             }
27394         }else{
27395             
27396             if(!s.animate){
27397                 s.resizingEl.setHeight(newSize);
27398                 if(onComplete){
27399                     onComplete(s, newSize);
27400                 }
27401             }else{
27402                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27403             }
27404         }
27405     }
27406 };
27407
27408 /** 
27409  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27410  * @extends Roo.SplitBar.BasicLayoutAdapter
27411  * Adapter that  moves the splitter element to align with the resized sizing element. 
27412  * Used with an absolute positioned SplitBar.
27413  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27414  * document.body, make sure you assign an id to the body element.
27415  */
27416 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27417     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27418     this.container = Roo.get(container);
27419 };
27420
27421 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27422     init : function(s){
27423         this.basic.init(s);
27424     },
27425     
27426     getElementSize : function(s){
27427         return this.basic.getElementSize(s);
27428     },
27429     
27430     setElementSize : function(s, newSize, onComplete){
27431         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27432     },
27433     
27434     moveSplitter : function(s){
27435         var yes = Roo.SplitBar;
27436         switch(s.placement){
27437             case yes.LEFT:
27438                 s.el.setX(s.resizingEl.getRight());
27439                 break;
27440             case yes.RIGHT:
27441                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27442                 break;
27443             case yes.TOP:
27444                 s.el.setY(s.resizingEl.getBottom());
27445                 break;
27446             case yes.BOTTOM:
27447                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27448                 break;
27449         }
27450     }
27451 };
27452
27453 /**
27454  * Orientation constant - Create a vertical SplitBar
27455  * @static
27456  * @type Number
27457  */
27458 Roo.SplitBar.VERTICAL = 1;
27459
27460 /**
27461  * Orientation constant - Create a horizontal SplitBar
27462  * @static
27463  * @type Number
27464  */
27465 Roo.SplitBar.HORIZONTAL = 2;
27466
27467 /**
27468  * Placement constant - The resizing element is to the left of the splitter element
27469  * @static
27470  * @type Number
27471  */
27472 Roo.SplitBar.LEFT = 1;
27473
27474 /**
27475  * Placement constant - The resizing element is to the right of the splitter element
27476  * @static
27477  * @type Number
27478  */
27479 Roo.SplitBar.RIGHT = 2;
27480
27481 /**
27482  * Placement constant - The resizing element is positioned above the splitter element
27483  * @static
27484  * @type Number
27485  */
27486 Roo.SplitBar.TOP = 3;
27487
27488 /**
27489  * Placement constant - The resizing element is positioned under splitter element
27490  * @static
27491  * @type Number
27492  */
27493 Roo.SplitBar.BOTTOM = 4;
27494 /*
27495  * Based on:
27496  * Ext JS Library 1.1.1
27497  * Copyright(c) 2006-2007, Ext JS, LLC.
27498  *
27499  * Originally Released Under LGPL - original licence link has changed is not relivant.
27500  *
27501  * Fork - LGPL
27502  * <script type="text/javascript">
27503  */
27504
27505 /**
27506  * @class Roo.View
27507  * @extends Roo.util.Observable
27508  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27509  * This class also supports single and multi selection modes. <br>
27510  * Create a data model bound view:
27511  <pre><code>
27512  var store = new Roo.data.Store(...);
27513
27514  var view = new Roo.View({
27515     el : "my-element",
27516     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27517  
27518     singleSelect: true,
27519     selectedClass: "ydataview-selected",
27520     store: store
27521  });
27522
27523  // listen for node click?
27524  view.on("click", function(vw, index, node, e){
27525  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27526  });
27527
27528  // load XML data
27529  dataModel.load("foobar.xml");
27530  </code></pre>
27531  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27532  * <br><br>
27533  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27534  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27535  * 
27536  * Note: old style constructor is still suported (container, template, config)
27537  * 
27538  * @constructor
27539  * Create a new View
27540  * @param {Object} config The config object
27541  * 
27542  */
27543 Roo.View = function(config, depreciated_tpl, depreciated_config){
27544     
27545     this.parent = false;
27546     
27547     if (typeof(depreciated_tpl) == 'undefined') {
27548         // new way.. - universal constructor.
27549         Roo.apply(this, config);
27550         this.el  = Roo.get(this.el);
27551     } else {
27552         // old format..
27553         this.el  = Roo.get(config);
27554         this.tpl = depreciated_tpl;
27555         Roo.apply(this, depreciated_config);
27556     }
27557     this.wrapEl  = this.el.wrap().wrap();
27558     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27559     
27560     
27561     if(typeof(this.tpl) == "string"){
27562         this.tpl = new Roo.Template(this.tpl);
27563     } else {
27564         // support xtype ctors..
27565         this.tpl = new Roo.factory(this.tpl, Roo);
27566     }
27567     
27568     
27569     this.tpl.compile();
27570     
27571     /** @private */
27572     this.addEvents({
27573         /**
27574          * @event beforeclick
27575          * Fires before a click is processed. Returns false to cancel the default action.
27576          * @param {Roo.View} this
27577          * @param {Number} index The index of the target node
27578          * @param {HTMLElement} node The target node
27579          * @param {Roo.EventObject} e The raw event object
27580          */
27581             "beforeclick" : true,
27582         /**
27583          * @event click
27584          * Fires when a template node is clicked.
27585          * @param {Roo.View} this
27586          * @param {Number} index The index of the target node
27587          * @param {HTMLElement} node The target node
27588          * @param {Roo.EventObject} e The raw event object
27589          */
27590             "click" : true,
27591         /**
27592          * @event dblclick
27593          * Fires when a template node is double clicked.
27594          * @param {Roo.View} this
27595          * @param {Number} index The index of the target node
27596          * @param {HTMLElement} node The target node
27597          * @param {Roo.EventObject} e The raw event object
27598          */
27599             "dblclick" : true,
27600         /**
27601          * @event contextmenu
27602          * Fires when a template node is right clicked.
27603          * @param {Roo.View} this
27604          * @param {Number} index The index of the target node
27605          * @param {HTMLElement} node The target node
27606          * @param {Roo.EventObject} e The raw event object
27607          */
27608             "contextmenu" : true,
27609         /**
27610          * @event selectionchange
27611          * Fires when the selected nodes change.
27612          * @param {Roo.View} this
27613          * @param {Array} selections Array of the selected nodes
27614          */
27615             "selectionchange" : true,
27616     
27617         /**
27618          * @event beforeselect
27619          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27620          * @param {Roo.View} this
27621          * @param {HTMLElement} node The node to be selected
27622          * @param {Array} selections Array of currently selected nodes
27623          */
27624             "beforeselect" : true,
27625         /**
27626          * @event preparedata
27627          * Fires on every row to render, to allow you to change the data.
27628          * @param {Roo.View} this
27629          * @param {Object} data to be rendered (change this)
27630          */
27631           "preparedata" : true
27632           
27633           
27634         });
27635
27636
27637
27638     this.el.on({
27639         "click": this.onClick,
27640         "dblclick": this.onDblClick,
27641         "contextmenu": this.onContextMenu,
27642         scope:this
27643     });
27644
27645     this.selections = [];
27646     this.nodes = [];
27647     this.cmp = new Roo.CompositeElementLite([]);
27648     if(this.store){
27649         this.store = Roo.factory(this.store, Roo.data);
27650         this.setStore(this.store, true);
27651     }
27652     
27653     if ( this.footer && this.footer.xtype) {
27654            
27655          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27656         
27657         this.footer.dataSource = this.store;
27658         this.footer.container = fctr;
27659         this.footer = Roo.factory(this.footer, Roo);
27660         fctr.insertFirst(this.el);
27661         
27662         // this is a bit insane - as the paging toolbar seems to detach the el..
27663 //        dom.parentNode.parentNode.parentNode
27664          // they get detached?
27665     }
27666     
27667     
27668     Roo.View.superclass.constructor.call(this);
27669     
27670     
27671 };
27672
27673 Roo.extend(Roo.View, Roo.util.Observable, {
27674     
27675      /**
27676      * @cfg {Roo.data.Store} store Data store to load data from.
27677      */
27678     store : false,
27679     
27680     /**
27681      * @cfg {String|Roo.Element} el The container element.
27682      */
27683     el : '',
27684     
27685     /**
27686      * @cfg {String|Roo.Template} tpl The template used by this View 
27687      */
27688     tpl : false,
27689     /**
27690      * @cfg {String} dataName the named area of the template to use as the data area
27691      *                          Works with domtemplates roo-name="name"
27692      */
27693     dataName: false,
27694     /**
27695      * @cfg {String} selectedClass The css class to add to selected nodes
27696      */
27697     selectedClass : "x-view-selected",
27698      /**
27699      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27700      */
27701     emptyText : "",
27702     
27703     /**
27704      * @cfg {String} text to display on mask (default Loading)
27705      */
27706     mask : false,
27707     /**
27708      * @cfg {Boolean} multiSelect Allow multiple selection
27709      */
27710     multiSelect : false,
27711     /**
27712      * @cfg {Boolean} singleSelect Allow single selection
27713      */
27714     singleSelect:  false,
27715     
27716     /**
27717      * @cfg {Boolean} toggleSelect - selecting 
27718      */
27719     toggleSelect : false,
27720     
27721     /**
27722      * @cfg {Boolean} tickable - selecting 
27723      */
27724     tickable : false,
27725     
27726     /**
27727      * Returns the element this view is bound to.
27728      * @return {Roo.Element}
27729      */
27730     getEl : function(){
27731         return this.wrapEl;
27732     },
27733     
27734     
27735
27736     /**
27737      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27738      */
27739     refresh : function(){
27740         //Roo.log('refresh');
27741         var t = this.tpl;
27742         
27743         // if we are using something like 'domtemplate', then
27744         // the what gets used is:
27745         // t.applySubtemplate(NAME, data, wrapping data..)
27746         // the outer template then get' applied with
27747         //     the store 'extra data'
27748         // and the body get's added to the
27749         //      roo-name="data" node?
27750         //      <span class='roo-tpl-{name}'></span> ?????
27751         
27752         
27753         
27754         this.clearSelections();
27755         this.el.update("");
27756         var html = [];
27757         var records = this.store.getRange();
27758         if(records.length < 1) {
27759             
27760             // is this valid??  = should it render a template??
27761             
27762             this.el.update(this.emptyText);
27763             return;
27764         }
27765         var el = this.el;
27766         if (this.dataName) {
27767             this.el.update(t.apply(this.store.meta)); //????
27768             el = this.el.child('.roo-tpl-' + this.dataName);
27769         }
27770         
27771         for(var i = 0, len = records.length; i < len; i++){
27772             var data = this.prepareData(records[i].data, i, records[i]);
27773             this.fireEvent("preparedata", this, data, i, records[i]);
27774             
27775             var d = Roo.apply({}, data);
27776             
27777             if(this.tickable){
27778                 Roo.apply(d, {'roo-id' : Roo.id()});
27779                 
27780                 var _this = this;
27781             
27782                 Roo.each(this.parent.item, function(item){
27783                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27784                         return;
27785                     }
27786                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27787                 });
27788             }
27789             
27790             html[html.length] = Roo.util.Format.trim(
27791                 this.dataName ?
27792                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27793                     t.apply(d)
27794             );
27795         }
27796         
27797         
27798         
27799         el.update(html.join(""));
27800         this.nodes = el.dom.childNodes;
27801         this.updateIndexes(0);
27802     },
27803     
27804
27805     /**
27806      * Function to override to reformat the data that is sent to
27807      * the template for each node.
27808      * DEPRICATED - use the preparedata event handler.
27809      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27810      * a JSON object for an UpdateManager bound view).
27811      */
27812     prepareData : function(data, index, record)
27813     {
27814         this.fireEvent("preparedata", this, data, index, record);
27815         return data;
27816     },
27817
27818     onUpdate : function(ds, record){
27819         // Roo.log('on update');   
27820         this.clearSelections();
27821         var index = this.store.indexOf(record);
27822         var n = this.nodes[index];
27823         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27824         n.parentNode.removeChild(n);
27825         this.updateIndexes(index, index);
27826     },
27827
27828     
27829     
27830 // --------- FIXME     
27831     onAdd : function(ds, records, index)
27832     {
27833         //Roo.log(['on Add', ds, records, index] );        
27834         this.clearSelections();
27835         if(this.nodes.length == 0){
27836             this.refresh();
27837             return;
27838         }
27839         var n = this.nodes[index];
27840         for(var i = 0, len = records.length; i < len; i++){
27841             var d = this.prepareData(records[i].data, i, records[i]);
27842             if(n){
27843                 this.tpl.insertBefore(n, d);
27844             }else{
27845                 
27846                 this.tpl.append(this.el, d);
27847             }
27848         }
27849         this.updateIndexes(index);
27850     },
27851
27852     onRemove : function(ds, record, index){
27853        // Roo.log('onRemove');
27854         this.clearSelections();
27855         var el = this.dataName  ?
27856             this.el.child('.roo-tpl-' + this.dataName) :
27857             this.el; 
27858         
27859         el.dom.removeChild(this.nodes[index]);
27860         this.updateIndexes(index);
27861     },
27862
27863     /**
27864      * Refresh an individual node.
27865      * @param {Number} index
27866      */
27867     refreshNode : function(index){
27868         this.onUpdate(this.store, this.store.getAt(index));
27869     },
27870
27871     updateIndexes : function(startIndex, endIndex){
27872         var ns = this.nodes;
27873         startIndex = startIndex || 0;
27874         endIndex = endIndex || ns.length - 1;
27875         for(var i = startIndex; i <= endIndex; i++){
27876             ns[i].nodeIndex = i;
27877         }
27878     },
27879
27880     /**
27881      * Changes the data store this view uses and refresh the view.
27882      * @param {Store} store
27883      */
27884     setStore : function(store, initial){
27885         if(!initial && this.store){
27886             this.store.un("datachanged", this.refresh);
27887             this.store.un("add", this.onAdd);
27888             this.store.un("remove", this.onRemove);
27889             this.store.un("update", this.onUpdate);
27890             this.store.un("clear", this.refresh);
27891             this.store.un("beforeload", this.onBeforeLoad);
27892             this.store.un("load", this.onLoad);
27893             this.store.un("loadexception", this.onLoad);
27894         }
27895         if(store){
27896           
27897             store.on("datachanged", this.refresh, this);
27898             store.on("add", this.onAdd, this);
27899             store.on("remove", this.onRemove, this);
27900             store.on("update", this.onUpdate, this);
27901             store.on("clear", this.refresh, this);
27902             store.on("beforeload", this.onBeforeLoad, this);
27903             store.on("load", this.onLoad, this);
27904             store.on("loadexception", this.onLoad, this);
27905         }
27906         
27907         if(store){
27908             this.refresh();
27909         }
27910     },
27911     /**
27912      * onbeforeLoad - masks the loading area.
27913      *
27914      */
27915     onBeforeLoad : function(store,opts)
27916     {
27917          //Roo.log('onBeforeLoad');   
27918         if (!opts.add) {
27919             this.el.update("");
27920         }
27921         this.el.mask(this.mask ? this.mask : "Loading" ); 
27922     },
27923     onLoad : function ()
27924     {
27925         this.el.unmask();
27926     },
27927     
27928
27929     /**
27930      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27931      * @param {HTMLElement} node
27932      * @return {HTMLElement} The template node
27933      */
27934     findItemFromChild : function(node){
27935         var el = this.dataName  ?
27936             this.el.child('.roo-tpl-' + this.dataName,true) :
27937             this.el.dom; 
27938         
27939         if(!node || node.parentNode == el){
27940                     return node;
27941             }
27942             var p = node.parentNode;
27943             while(p && p != el){
27944             if(p.parentNode == el){
27945                 return p;
27946             }
27947             p = p.parentNode;
27948         }
27949             return null;
27950     },
27951
27952     /** @ignore */
27953     onClick : function(e){
27954         var item = this.findItemFromChild(e.getTarget());
27955         if(item){
27956             var index = this.indexOf(item);
27957             if(this.onItemClick(item, index, e) !== false){
27958                 this.fireEvent("click", this, index, item, e);
27959             }
27960         }else{
27961             this.clearSelections();
27962         }
27963     },
27964
27965     /** @ignore */
27966     onContextMenu : function(e){
27967         var item = this.findItemFromChild(e.getTarget());
27968         if(item){
27969             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27970         }
27971     },
27972
27973     /** @ignore */
27974     onDblClick : function(e){
27975         var item = this.findItemFromChild(e.getTarget());
27976         if(item){
27977             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27978         }
27979     },
27980
27981     onItemClick : function(item, index, e)
27982     {
27983         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27984             return false;
27985         }
27986         if (this.toggleSelect) {
27987             var m = this.isSelected(item) ? 'unselect' : 'select';
27988             //Roo.log(m);
27989             var _t = this;
27990             _t[m](item, true, false);
27991             return true;
27992         }
27993         if(this.multiSelect || this.singleSelect){
27994             if(this.multiSelect && e.shiftKey && this.lastSelection){
27995                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27996             }else{
27997                 this.select(item, this.multiSelect && e.ctrlKey);
27998                 this.lastSelection = item;
27999             }
28000             
28001             if(!this.tickable){
28002                 e.preventDefault();
28003             }
28004             
28005         }
28006         return true;
28007     },
28008
28009     /**
28010      * Get the number of selected nodes.
28011      * @return {Number}
28012      */
28013     getSelectionCount : function(){
28014         return this.selections.length;
28015     },
28016
28017     /**
28018      * Get the currently selected nodes.
28019      * @return {Array} An array of HTMLElements
28020      */
28021     getSelectedNodes : function(){
28022         return this.selections;
28023     },
28024
28025     /**
28026      * Get the indexes of the selected nodes.
28027      * @return {Array}
28028      */
28029     getSelectedIndexes : function(){
28030         var indexes = [], s = this.selections;
28031         for(var i = 0, len = s.length; i < len; i++){
28032             indexes.push(s[i].nodeIndex);
28033         }
28034         return indexes;
28035     },
28036
28037     /**
28038      * Clear all selections
28039      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28040      */
28041     clearSelections : function(suppressEvent){
28042         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28043             this.cmp.elements = this.selections;
28044             this.cmp.removeClass(this.selectedClass);
28045             this.selections = [];
28046             if(!suppressEvent){
28047                 this.fireEvent("selectionchange", this, this.selections);
28048             }
28049         }
28050     },
28051
28052     /**
28053      * Returns true if the passed node is selected
28054      * @param {HTMLElement/Number} node The node or node index
28055      * @return {Boolean}
28056      */
28057     isSelected : function(node){
28058         var s = this.selections;
28059         if(s.length < 1){
28060             return false;
28061         }
28062         node = this.getNode(node);
28063         return s.indexOf(node) !== -1;
28064     },
28065
28066     /**
28067      * Selects nodes.
28068      * @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
28069      * @param {Boolean} keepExisting (optional) true to keep existing selections
28070      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28071      */
28072     select : function(nodeInfo, keepExisting, suppressEvent){
28073         if(nodeInfo instanceof Array){
28074             if(!keepExisting){
28075                 this.clearSelections(true);
28076             }
28077             for(var i = 0, len = nodeInfo.length; i < len; i++){
28078                 this.select(nodeInfo[i], true, true);
28079             }
28080             return;
28081         } 
28082         var node = this.getNode(nodeInfo);
28083         if(!node || this.isSelected(node)){
28084             return; // already selected.
28085         }
28086         if(!keepExisting){
28087             this.clearSelections(true);
28088         }
28089         
28090         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28091             Roo.fly(node).addClass(this.selectedClass);
28092             this.selections.push(node);
28093             if(!suppressEvent){
28094                 this.fireEvent("selectionchange", this, this.selections);
28095             }
28096         }
28097         
28098         
28099     },
28100       /**
28101      * Unselects nodes.
28102      * @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
28103      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28104      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28105      */
28106     unselect : function(nodeInfo, keepExisting, suppressEvent)
28107     {
28108         if(nodeInfo instanceof Array){
28109             Roo.each(this.selections, function(s) {
28110                 this.unselect(s, nodeInfo);
28111             }, this);
28112             return;
28113         }
28114         var node = this.getNode(nodeInfo);
28115         if(!node || !this.isSelected(node)){
28116             //Roo.log("not selected");
28117             return; // not selected.
28118         }
28119         // fireevent???
28120         var ns = [];
28121         Roo.each(this.selections, function(s) {
28122             if (s == node ) {
28123                 Roo.fly(node).removeClass(this.selectedClass);
28124
28125                 return;
28126             }
28127             ns.push(s);
28128         },this);
28129         
28130         this.selections= ns;
28131         this.fireEvent("selectionchange", this, this.selections);
28132     },
28133
28134     /**
28135      * Gets a template node.
28136      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28137      * @return {HTMLElement} The node or null if it wasn't found
28138      */
28139     getNode : function(nodeInfo){
28140         if(typeof nodeInfo == "string"){
28141             return document.getElementById(nodeInfo);
28142         }else if(typeof nodeInfo == "number"){
28143             return this.nodes[nodeInfo];
28144         }
28145         return nodeInfo;
28146     },
28147
28148     /**
28149      * Gets a range template nodes.
28150      * @param {Number} startIndex
28151      * @param {Number} endIndex
28152      * @return {Array} An array of nodes
28153      */
28154     getNodes : function(start, end){
28155         var ns = this.nodes;
28156         start = start || 0;
28157         end = typeof end == "undefined" ? ns.length - 1 : end;
28158         var nodes = [];
28159         if(start <= end){
28160             for(var i = start; i <= end; i++){
28161                 nodes.push(ns[i]);
28162             }
28163         } else{
28164             for(var i = start; i >= end; i--){
28165                 nodes.push(ns[i]);
28166             }
28167         }
28168         return nodes;
28169     },
28170
28171     /**
28172      * Finds the index of the passed node
28173      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28174      * @return {Number} The index of the node or -1
28175      */
28176     indexOf : function(node){
28177         node = this.getNode(node);
28178         if(typeof node.nodeIndex == "number"){
28179             return node.nodeIndex;
28180         }
28181         var ns = this.nodes;
28182         for(var i = 0, len = ns.length; i < len; i++){
28183             if(ns[i] == node){
28184                 return i;
28185             }
28186         }
28187         return -1;
28188     }
28189 });
28190 /*
28191  * Based on:
28192  * Ext JS Library 1.1.1
28193  * Copyright(c) 2006-2007, Ext JS, LLC.
28194  *
28195  * Originally Released Under LGPL - original licence link has changed is not relivant.
28196  *
28197  * Fork - LGPL
28198  * <script type="text/javascript">
28199  */
28200
28201 /**
28202  * @class Roo.JsonView
28203  * @extends Roo.View
28204  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28205 <pre><code>
28206 var view = new Roo.JsonView({
28207     container: "my-element",
28208     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28209     multiSelect: true, 
28210     jsonRoot: "data" 
28211 });
28212
28213 // listen for node click?
28214 view.on("click", function(vw, index, node, e){
28215     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28216 });
28217
28218 // direct load of JSON data
28219 view.load("foobar.php");
28220
28221 // Example from my blog list
28222 var tpl = new Roo.Template(
28223     '&lt;div class="entry"&gt;' +
28224     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28225     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28226     "&lt;/div&gt;&lt;hr /&gt;"
28227 );
28228
28229 var moreView = new Roo.JsonView({
28230     container :  "entry-list", 
28231     template : tpl,
28232     jsonRoot: "posts"
28233 });
28234 moreView.on("beforerender", this.sortEntries, this);
28235 moreView.load({
28236     url: "/blog/get-posts.php",
28237     params: "allposts=true",
28238     text: "Loading Blog Entries..."
28239 });
28240 </code></pre>
28241
28242 * Note: old code is supported with arguments : (container, template, config)
28243
28244
28245  * @constructor
28246  * Create a new JsonView
28247  * 
28248  * @param {Object} config The config object
28249  * 
28250  */
28251 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28252     
28253     
28254     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28255
28256     var um = this.el.getUpdateManager();
28257     um.setRenderer(this);
28258     um.on("update", this.onLoad, this);
28259     um.on("failure", this.onLoadException, this);
28260
28261     /**
28262      * @event beforerender
28263      * Fires before rendering of the downloaded JSON data.
28264      * @param {Roo.JsonView} this
28265      * @param {Object} data The JSON data loaded
28266      */
28267     /**
28268      * @event load
28269      * Fires when data is loaded.
28270      * @param {Roo.JsonView} this
28271      * @param {Object} data The JSON data loaded
28272      * @param {Object} response The raw Connect response object
28273      */
28274     /**
28275      * @event loadexception
28276      * Fires when loading fails.
28277      * @param {Roo.JsonView} this
28278      * @param {Object} response The raw Connect response object
28279      */
28280     this.addEvents({
28281         'beforerender' : true,
28282         'load' : true,
28283         'loadexception' : true
28284     });
28285 };
28286 Roo.extend(Roo.JsonView, Roo.View, {
28287     /**
28288      * @type {String} The root property in the loaded JSON object that contains the data
28289      */
28290     jsonRoot : "",
28291
28292     /**
28293      * Refreshes the view.
28294      */
28295     refresh : function(){
28296         this.clearSelections();
28297         this.el.update("");
28298         var html = [];
28299         var o = this.jsonData;
28300         if(o && o.length > 0){
28301             for(var i = 0, len = o.length; i < len; i++){
28302                 var data = this.prepareData(o[i], i, o);
28303                 html[html.length] = this.tpl.apply(data);
28304             }
28305         }else{
28306             html.push(this.emptyText);
28307         }
28308         this.el.update(html.join(""));
28309         this.nodes = this.el.dom.childNodes;
28310         this.updateIndexes(0);
28311     },
28312
28313     /**
28314      * 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.
28315      * @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:
28316      <pre><code>
28317      view.load({
28318          url: "your-url.php",
28319          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28320          callback: yourFunction,
28321          scope: yourObject, //(optional scope)
28322          discardUrl: false,
28323          nocache: false,
28324          text: "Loading...",
28325          timeout: 30,
28326          scripts: false
28327      });
28328      </code></pre>
28329      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28330      * 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.
28331      * @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}
28332      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28333      * @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.
28334      */
28335     load : function(){
28336         var um = this.el.getUpdateManager();
28337         um.update.apply(um, arguments);
28338     },
28339
28340     // note - render is a standard framework call...
28341     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28342     render : function(el, response){
28343         
28344         this.clearSelections();
28345         this.el.update("");
28346         var o;
28347         try{
28348             if (response != '') {
28349                 o = Roo.util.JSON.decode(response.responseText);
28350                 if(this.jsonRoot){
28351                     
28352                     o = o[this.jsonRoot];
28353                 }
28354             }
28355         } catch(e){
28356         }
28357         /**
28358          * The current JSON data or null
28359          */
28360         this.jsonData = o;
28361         this.beforeRender();
28362         this.refresh();
28363     },
28364
28365 /**
28366  * Get the number of records in the current JSON dataset
28367  * @return {Number}
28368  */
28369     getCount : function(){
28370         return this.jsonData ? this.jsonData.length : 0;
28371     },
28372
28373 /**
28374  * Returns the JSON object for the specified node(s)
28375  * @param {HTMLElement/Array} node The node or an array of nodes
28376  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28377  * you get the JSON object for the node
28378  */
28379     getNodeData : function(node){
28380         if(node instanceof Array){
28381             var data = [];
28382             for(var i = 0, len = node.length; i < len; i++){
28383                 data.push(this.getNodeData(node[i]));
28384             }
28385             return data;
28386         }
28387         return this.jsonData[this.indexOf(node)] || null;
28388     },
28389
28390     beforeRender : function(){
28391         this.snapshot = this.jsonData;
28392         if(this.sortInfo){
28393             this.sort.apply(this, this.sortInfo);
28394         }
28395         this.fireEvent("beforerender", this, this.jsonData);
28396     },
28397
28398     onLoad : function(el, o){
28399         this.fireEvent("load", this, this.jsonData, o);
28400     },
28401
28402     onLoadException : function(el, o){
28403         this.fireEvent("loadexception", this, o);
28404     },
28405
28406 /**
28407  * Filter the data by a specific property.
28408  * @param {String} property A property on your JSON objects
28409  * @param {String/RegExp} value Either string that the property values
28410  * should start with, or a RegExp to test against the property
28411  */
28412     filter : function(property, value){
28413         if(this.jsonData){
28414             var data = [];
28415             var ss = this.snapshot;
28416             if(typeof value == "string"){
28417                 var vlen = value.length;
28418                 if(vlen == 0){
28419                     this.clearFilter();
28420                     return;
28421                 }
28422                 value = value.toLowerCase();
28423                 for(var i = 0, len = ss.length; i < len; i++){
28424                     var o = ss[i];
28425                     if(o[property].substr(0, vlen).toLowerCase() == value){
28426                         data.push(o);
28427                     }
28428                 }
28429             } else if(value.exec){ // regex?
28430                 for(var i = 0, len = ss.length; i < len; i++){
28431                     var o = ss[i];
28432                     if(value.test(o[property])){
28433                         data.push(o);
28434                     }
28435                 }
28436             } else{
28437                 return;
28438             }
28439             this.jsonData = data;
28440             this.refresh();
28441         }
28442     },
28443
28444 /**
28445  * Filter by a function. The passed function will be called with each
28446  * object in the current dataset. If the function returns true the value is kept,
28447  * otherwise it is filtered.
28448  * @param {Function} fn
28449  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28450  */
28451     filterBy : function(fn, scope){
28452         if(this.jsonData){
28453             var data = [];
28454             var ss = this.snapshot;
28455             for(var i = 0, len = ss.length; i < len; i++){
28456                 var o = ss[i];
28457                 if(fn.call(scope || this, o)){
28458                     data.push(o);
28459                 }
28460             }
28461             this.jsonData = data;
28462             this.refresh();
28463         }
28464     },
28465
28466 /**
28467  * Clears the current filter.
28468  */
28469     clearFilter : function(){
28470         if(this.snapshot && this.jsonData != this.snapshot){
28471             this.jsonData = this.snapshot;
28472             this.refresh();
28473         }
28474     },
28475
28476
28477 /**
28478  * Sorts the data for this view and refreshes it.
28479  * @param {String} property A property on your JSON objects to sort on
28480  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28481  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28482  */
28483     sort : function(property, dir, sortType){
28484         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28485         if(this.jsonData){
28486             var p = property;
28487             var dsc = dir && dir.toLowerCase() == "desc";
28488             var f = function(o1, o2){
28489                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28490                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28491                 ;
28492                 if(v1 < v2){
28493                     return dsc ? +1 : -1;
28494                 } else if(v1 > v2){
28495                     return dsc ? -1 : +1;
28496                 } else{
28497                     return 0;
28498                 }
28499             };
28500             this.jsonData.sort(f);
28501             this.refresh();
28502             if(this.jsonData != this.snapshot){
28503                 this.snapshot.sort(f);
28504             }
28505         }
28506     }
28507 });/*
28508  * Based on:
28509  * Ext JS Library 1.1.1
28510  * Copyright(c) 2006-2007, Ext JS, LLC.
28511  *
28512  * Originally Released Under LGPL - original licence link has changed is not relivant.
28513  *
28514  * Fork - LGPL
28515  * <script type="text/javascript">
28516  */
28517  
28518
28519 /**
28520  * @class Roo.ColorPalette
28521  * @extends Roo.Component
28522  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28523  * Here's an example of typical usage:
28524  * <pre><code>
28525 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28526 cp.render('my-div');
28527
28528 cp.on('select', function(palette, selColor){
28529     // do something with selColor
28530 });
28531 </code></pre>
28532  * @constructor
28533  * Create a new ColorPalette
28534  * @param {Object} config The config object
28535  */
28536 Roo.ColorPalette = function(config){
28537     Roo.ColorPalette.superclass.constructor.call(this, config);
28538     this.addEvents({
28539         /**
28540              * @event select
28541              * Fires when a color is selected
28542              * @param {ColorPalette} this
28543              * @param {String} color The 6-digit color hex code (without the # symbol)
28544              */
28545         select: true
28546     });
28547
28548     if(this.handler){
28549         this.on("select", this.handler, this.scope, true);
28550     }
28551 };
28552 Roo.extend(Roo.ColorPalette, Roo.Component, {
28553     /**
28554      * @cfg {String} itemCls
28555      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28556      */
28557     itemCls : "x-color-palette",
28558     /**
28559      * @cfg {String} value
28560      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28561      * the hex codes are case-sensitive.
28562      */
28563     value : null,
28564     clickEvent:'click',
28565     // private
28566     ctype: "Roo.ColorPalette",
28567
28568     /**
28569      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28570      */
28571     allowReselect : false,
28572
28573     /**
28574      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28575      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28576      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28577      * of colors with the width setting until the box is symmetrical.</p>
28578      * <p>You can override individual colors if needed:</p>
28579      * <pre><code>
28580 var cp = new Roo.ColorPalette();
28581 cp.colors[0] = "FF0000";  // change the first box to red
28582 </code></pre>
28583
28584 Or you can provide a custom array of your own for complete control:
28585 <pre><code>
28586 var cp = new Roo.ColorPalette();
28587 cp.colors = ["000000", "993300", "333300"];
28588 </code></pre>
28589      * @type Array
28590      */
28591     colors : [
28592         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28593         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28594         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28595         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28596         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28597     ],
28598
28599     // private
28600     onRender : function(container, position){
28601         var t = new Roo.MasterTemplate(
28602             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28603         );
28604         var c = this.colors;
28605         for(var i = 0, len = c.length; i < len; i++){
28606             t.add([c[i]]);
28607         }
28608         var el = document.createElement("div");
28609         el.className = this.itemCls;
28610         t.overwrite(el);
28611         container.dom.insertBefore(el, position);
28612         this.el = Roo.get(el);
28613         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28614         if(this.clickEvent != 'click'){
28615             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28616         }
28617     },
28618
28619     // private
28620     afterRender : function(){
28621         Roo.ColorPalette.superclass.afterRender.call(this);
28622         if(this.value){
28623             var s = this.value;
28624             this.value = null;
28625             this.select(s);
28626         }
28627     },
28628
28629     // private
28630     handleClick : function(e, t){
28631         e.preventDefault();
28632         if(!this.disabled){
28633             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28634             this.select(c.toUpperCase());
28635         }
28636     },
28637
28638     /**
28639      * Selects the specified color in the palette (fires the select event)
28640      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28641      */
28642     select : function(color){
28643         color = color.replace("#", "");
28644         if(color != this.value || this.allowReselect){
28645             var el = this.el;
28646             if(this.value){
28647                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28648             }
28649             el.child("a.color-"+color).addClass("x-color-palette-sel");
28650             this.value = color;
28651             this.fireEvent("select", this, color);
28652         }
28653     }
28654 });/*
28655  * Based on:
28656  * Ext JS Library 1.1.1
28657  * Copyright(c) 2006-2007, Ext JS, LLC.
28658  *
28659  * Originally Released Under LGPL - original licence link has changed is not relivant.
28660  *
28661  * Fork - LGPL
28662  * <script type="text/javascript">
28663  */
28664  
28665 /**
28666  * @class Roo.DatePicker
28667  * @extends Roo.Component
28668  * Simple date picker class.
28669  * @constructor
28670  * Create a new DatePicker
28671  * @param {Object} config The config object
28672  */
28673 Roo.DatePicker = function(config){
28674     Roo.DatePicker.superclass.constructor.call(this, config);
28675
28676     this.value = config && config.value ?
28677                  config.value.clearTime() : new Date().clearTime();
28678
28679     this.addEvents({
28680         /**
28681              * @event select
28682              * Fires when a date is selected
28683              * @param {DatePicker} this
28684              * @param {Date} date The selected date
28685              */
28686         'select': true,
28687         /**
28688              * @event monthchange
28689              * Fires when the displayed month changes 
28690              * @param {DatePicker} this
28691              * @param {Date} date The selected month
28692              */
28693         'monthchange': true
28694     });
28695
28696     if(this.handler){
28697         this.on("select", this.handler,  this.scope || this);
28698     }
28699     // build the disabledDatesRE
28700     if(!this.disabledDatesRE && this.disabledDates){
28701         var dd = this.disabledDates;
28702         var re = "(?:";
28703         for(var i = 0; i < dd.length; i++){
28704             re += dd[i];
28705             if(i != dd.length-1) {
28706                 re += "|";
28707             }
28708         }
28709         this.disabledDatesRE = new RegExp(re + ")");
28710     }
28711 };
28712
28713 Roo.extend(Roo.DatePicker, Roo.Component, {
28714     /**
28715      * @cfg {String} todayText
28716      * The text to display on the button that selects the current date (defaults to "Today")
28717      */
28718     todayText : "Today",
28719     /**
28720      * @cfg {String} okText
28721      * The text to display on the ok button
28722      */
28723     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28724     /**
28725      * @cfg {String} cancelText
28726      * The text to display on the cancel button
28727      */
28728     cancelText : "Cancel",
28729     /**
28730      * @cfg {String} todayTip
28731      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28732      */
28733     todayTip : "{0} (Spacebar)",
28734     /**
28735      * @cfg {Date} minDate
28736      * Minimum allowable date (JavaScript date object, defaults to null)
28737      */
28738     minDate : null,
28739     /**
28740      * @cfg {Date} maxDate
28741      * Maximum allowable date (JavaScript date object, defaults to null)
28742      */
28743     maxDate : null,
28744     /**
28745      * @cfg {String} minText
28746      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28747      */
28748     minText : "This date is before the minimum date",
28749     /**
28750      * @cfg {String} maxText
28751      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28752      */
28753     maxText : "This date is after the maximum date",
28754     /**
28755      * @cfg {String} format
28756      * The default date format string which can be overriden for localization support.  The format must be
28757      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28758      */
28759     format : "m/d/y",
28760     /**
28761      * @cfg {Array} disabledDays
28762      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28763      */
28764     disabledDays : null,
28765     /**
28766      * @cfg {String} disabledDaysText
28767      * The tooltip to display when the date falls on a disabled day (defaults to "")
28768      */
28769     disabledDaysText : "",
28770     /**
28771      * @cfg {RegExp} disabledDatesRE
28772      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28773      */
28774     disabledDatesRE : null,
28775     /**
28776      * @cfg {String} disabledDatesText
28777      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28778      */
28779     disabledDatesText : "",
28780     /**
28781      * @cfg {Boolean} constrainToViewport
28782      * True to constrain the date picker to the viewport (defaults to true)
28783      */
28784     constrainToViewport : true,
28785     /**
28786      * @cfg {Array} monthNames
28787      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28788      */
28789     monthNames : Date.monthNames,
28790     /**
28791      * @cfg {Array} dayNames
28792      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28793      */
28794     dayNames : Date.dayNames,
28795     /**
28796      * @cfg {String} nextText
28797      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28798      */
28799     nextText: 'Next Month (Control+Right)',
28800     /**
28801      * @cfg {String} prevText
28802      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28803      */
28804     prevText: 'Previous Month (Control+Left)',
28805     /**
28806      * @cfg {String} monthYearText
28807      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28808      */
28809     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28810     /**
28811      * @cfg {Number} startDay
28812      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28813      */
28814     startDay : 0,
28815     /**
28816      * @cfg {Bool} showClear
28817      * Show a clear button (usefull for date form elements that can be blank.)
28818      */
28819     
28820     showClear: false,
28821     
28822     /**
28823      * Sets the value of the date field
28824      * @param {Date} value The date to set
28825      */
28826     setValue : function(value){
28827         var old = this.value;
28828         
28829         if (typeof(value) == 'string') {
28830          
28831             value = Date.parseDate(value, this.format);
28832         }
28833         if (!value) {
28834             value = new Date();
28835         }
28836         
28837         this.value = value.clearTime(true);
28838         if(this.el){
28839             this.update(this.value);
28840         }
28841     },
28842
28843     /**
28844      * Gets the current selected value of the date field
28845      * @return {Date} The selected date
28846      */
28847     getValue : function(){
28848         return this.value;
28849     },
28850
28851     // private
28852     focus : function(){
28853         if(this.el){
28854             this.update(this.activeDate);
28855         }
28856     },
28857
28858     // privateval
28859     onRender : function(container, position){
28860         
28861         var m = [
28862              '<table cellspacing="0">',
28863                 '<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>',
28864                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28865         var dn = this.dayNames;
28866         for(var i = 0; i < 7; i++){
28867             var d = this.startDay+i;
28868             if(d > 6){
28869                 d = d-7;
28870             }
28871             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28872         }
28873         m[m.length] = "</tr></thead><tbody><tr>";
28874         for(var i = 0; i < 42; i++) {
28875             if(i % 7 == 0 && i != 0){
28876                 m[m.length] = "</tr><tr>";
28877             }
28878             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28879         }
28880         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28881             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28882
28883         var el = document.createElement("div");
28884         el.className = "x-date-picker";
28885         el.innerHTML = m.join("");
28886
28887         container.dom.insertBefore(el, position);
28888
28889         this.el = Roo.get(el);
28890         this.eventEl = Roo.get(el.firstChild);
28891
28892         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28893             handler: this.showPrevMonth,
28894             scope: this,
28895             preventDefault:true,
28896             stopDefault:true
28897         });
28898
28899         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28900             handler: this.showNextMonth,
28901             scope: this,
28902             preventDefault:true,
28903             stopDefault:true
28904         });
28905
28906         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28907
28908         this.monthPicker = this.el.down('div.x-date-mp');
28909         this.monthPicker.enableDisplayMode('block');
28910         
28911         var kn = new Roo.KeyNav(this.eventEl, {
28912             "left" : function(e){
28913                 e.ctrlKey ?
28914                     this.showPrevMonth() :
28915                     this.update(this.activeDate.add("d", -1));
28916             },
28917
28918             "right" : function(e){
28919                 e.ctrlKey ?
28920                     this.showNextMonth() :
28921                     this.update(this.activeDate.add("d", 1));
28922             },
28923
28924             "up" : function(e){
28925                 e.ctrlKey ?
28926                     this.showNextYear() :
28927                     this.update(this.activeDate.add("d", -7));
28928             },
28929
28930             "down" : function(e){
28931                 e.ctrlKey ?
28932                     this.showPrevYear() :
28933                     this.update(this.activeDate.add("d", 7));
28934             },
28935
28936             "pageUp" : function(e){
28937                 this.showNextMonth();
28938             },
28939
28940             "pageDown" : function(e){
28941                 this.showPrevMonth();
28942             },
28943
28944             "enter" : function(e){
28945                 e.stopPropagation();
28946                 return true;
28947             },
28948
28949             scope : this
28950         });
28951
28952         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28953
28954         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28955
28956         this.el.unselectable();
28957         
28958         this.cells = this.el.select("table.x-date-inner tbody td");
28959         this.textNodes = this.el.query("table.x-date-inner tbody span");
28960
28961         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28962             text: "&#160;",
28963             tooltip: this.monthYearText
28964         });
28965
28966         this.mbtn.on('click', this.showMonthPicker, this);
28967         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28968
28969
28970         var today = (new Date()).dateFormat(this.format);
28971         
28972         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28973         if (this.showClear) {
28974             baseTb.add( new Roo.Toolbar.Fill());
28975         }
28976         baseTb.add({
28977             text: String.format(this.todayText, today),
28978             tooltip: String.format(this.todayTip, today),
28979             handler: this.selectToday,
28980             scope: this
28981         });
28982         
28983         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28984             
28985         //});
28986         if (this.showClear) {
28987             
28988             baseTb.add( new Roo.Toolbar.Fill());
28989             baseTb.add({
28990                 text: '&#160;',
28991                 cls: 'x-btn-icon x-btn-clear',
28992                 handler: function() {
28993                     //this.value = '';
28994                     this.fireEvent("select", this, '');
28995                 },
28996                 scope: this
28997             });
28998         }
28999         
29000         
29001         if(Roo.isIE){
29002             this.el.repaint();
29003         }
29004         this.update(this.value);
29005     },
29006
29007     createMonthPicker : function(){
29008         if(!this.monthPicker.dom.firstChild){
29009             var buf = ['<table border="0" cellspacing="0">'];
29010             for(var i = 0; i < 6; i++){
29011                 buf.push(
29012                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29013                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29014                     i == 0 ?
29015                     '<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>' :
29016                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29017                 );
29018             }
29019             buf.push(
29020                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29021                     this.okText,
29022                     '</button><button type="button" class="x-date-mp-cancel">',
29023                     this.cancelText,
29024                     '</button></td></tr>',
29025                 '</table>'
29026             );
29027             this.monthPicker.update(buf.join(''));
29028             this.monthPicker.on('click', this.onMonthClick, this);
29029             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29030
29031             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29032             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29033
29034             this.mpMonths.each(function(m, a, i){
29035                 i += 1;
29036                 if((i%2) == 0){
29037                     m.dom.xmonth = 5 + Math.round(i * .5);
29038                 }else{
29039                     m.dom.xmonth = Math.round((i-1) * .5);
29040                 }
29041             });
29042         }
29043     },
29044
29045     showMonthPicker : function(){
29046         this.createMonthPicker();
29047         var size = this.el.getSize();
29048         this.monthPicker.setSize(size);
29049         this.monthPicker.child('table').setSize(size);
29050
29051         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29052         this.updateMPMonth(this.mpSelMonth);
29053         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29054         this.updateMPYear(this.mpSelYear);
29055
29056         this.monthPicker.slideIn('t', {duration:.2});
29057     },
29058
29059     updateMPYear : function(y){
29060         this.mpyear = y;
29061         var ys = this.mpYears.elements;
29062         for(var i = 1; i <= 10; i++){
29063             var td = ys[i-1], y2;
29064             if((i%2) == 0){
29065                 y2 = y + Math.round(i * .5);
29066                 td.firstChild.innerHTML = y2;
29067                 td.xyear = y2;
29068             }else{
29069                 y2 = y - (5-Math.round(i * .5));
29070                 td.firstChild.innerHTML = y2;
29071                 td.xyear = y2;
29072             }
29073             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29074         }
29075     },
29076
29077     updateMPMonth : function(sm){
29078         this.mpMonths.each(function(m, a, i){
29079             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29080         });
29081     },
29082
29083     selectMPMonth: function(m){
29084         
29085     },
29086
29087     onMonthClick : function(e, t){
29088         e.stopEvent();
29089         var el = new Roo.Element(t), pn;
29090         if(el.is('button.x-date-mp-cancel')){
29091             this.hideMonthPicker();
29092         }
29093         else if(el.is('button.x-date-mp-ok')){
29094             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29095             this.hideMonthPicker();
29096         }
29097         else if(pn = el.up('td.x-date-mp-month', 2)){
29098             this.mpMonths.removeClass('x-date-mp-sel');
29099             pn.addClass('x-date-mp-sel');
29100             this.mpSelMonth = pn.dom.xmonth;
29101         }
29102         else if(pn = el.up('td.x-date-mp-year', 2)){
29103             this.mpYears.removeClass('x-date-mp-sel');
29104             pn.addClass('x-date-mp-sel');
29105             this.mpSelYear = pn.dom.xyear;
29106         }
29107         else if(el.is('a.x-date-mp-prev')){
29108             this.updateMPYear(this.mpyear-10);
29109         }
29110         else if(el.is('a.x-date-mp-next')){
29111             this.updateMPYear(this.mpyear+10);
29112         }
29113     },
29114
29115     onMonthDblClick : function(e, t){
29116         e.stopEvent();
29117         var el = new Roo.Element(t), pn;
29118         if(pn = el.up('td.x-date-mp-month', 2)){
29119             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29120             this.hideMonthPicker();
29121         }
29122         else if(pn = el.up('td.x-date-mp-year', 2)){
29123             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29124             this.hideMonthPicker();
29125         }
29126     },
29127
29128     hideMonthPicker : function(disableAnim){
29129         if(this.monthPicker){
29130             if(disableAnim === true){
29131                 this.monthPicker.hide();
29132             }else{
29133                 this.monthPicker.slideOut('t', {duration:.2});
29134             }
29135         }
29136     },
29137
29138     // private
29139     showPrevMonth : function(e){
29140         this.update(this.activeDate.add("mo", -1));
29141     },
29142
29143     // private
29144     showNextMonth : function(e){
29145         this.update(this.activeDate.add("mo", 1));
29146     },
29147
29148     // private
29149     showPrevYear : function(){
29150         this.update(this.activeDate.add("y", -1));
29151     },
29152
29153     // private
29154     showNextYear : function(){
29155         this.update(this.activeDate.add("y", 1));
29156     },
29157
29158     // private
29159     handleMouseWheel : function(e){
29160         var delta = e.getWheelDelta();
29161         if(delta > 0){
29162             this.showPrevMonth();
29163             e.stopEvent();
29164         } else if(delta < 0){
29165             this.showNextMonth();
29166             e.stopEvent();
29167         }
29168     },
29169
29170     // private
29171     handleDateClick : function(e, t){
29172         e.stopEvent();
29173         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29174             this.setValue(new Date(t.dateValue));
29175             this.fireEvent("select", this, this.value);
29176         }
29177     },
29178
29179     // private
29180     selectToday : function(){
29181         this.setValue(new Date().clearTime());
29182         this.fireEvent("select", this, this.value);
29183     },
29184
29185     // private
29186     update : function(date)
29187     {
29188         var vd = this.activeDate;
29189         this.activeDate = date;
29190         if(vd && this.el){
29191             var t = date.getTime();
29192             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29193                 this.cells.removeClass("x-date-selected");
29194                 this.cells.each(function(c){
29195                    if(c.dom.firstChild.dateValue == t){
29196                        c.addClass("x-date-selected");
29197                        setTimeout(function(){
29198                             try{c.dom.firstChild.focus();}catch(e){}
29199                        }, 50);
29200                        return false;
29201                    }
29202                 });
29203                 return;
29204             }
29205         }
29206         
29207         var days = date.getDaysInMonth();
29208         var firstOfMonth = date.getFirstDateOfMonth();
29209         var startingPos = firstOfMonth.getDay()-this.startDay;
29210
29211         if(startingPos <= this.startDay){
29212             startingPos += 7;
29213         }
29214
29215         var pm = date.add("mo", -1);
29216         var prevStart = pm.getDaysInMonth()-startingPos;
29217
29218         var cells = this.cells.elements;
29219         var textEls = this.textNodes;
29220         days += startingPos;
29221
29222         // convert everything to numbers so it's fast
29223         var day = 86400000;
29224         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29225         var today = new Date().clearTime().getTime();
29226         var sel = date.clearTime().getTime();
29227         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29228         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29229         var ddMatch = this.disabledDatesRE;
29230         var ddText = this.disabledDatesText;
29231         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29232         var ddaysText = this.disabledDaysText;
29233         var format = this.format;
29234
29235         var setCellClass = function(cal, cell){
29236             cell.title = "";
29237             var t = d.getTime();
29238             cell.firstChild.dateValue = t;
29239             if(t == today){
29240                 cell.className += " x-date-today";
29241                 cell.title = cal.todayText;
29242             }
29243             if(t == sel){
29244                 cell.className += " x-date-selected";
29245                 setTimeout(function(){
29246                     try{cell.firstChild.focus();}catch(e){}
29247                 }, 50);
29248             }
29249             // disabling
29250             if(t < min) {
29251                 cell.className = " x-date-disabled";
29252                 cell.title = cal.minText;
29253                 return;
29254             }
29255             if(t > max) {
29256                 cell.className = " x-date-disabled";
29257                 cell.title = cal.maxText;
29258                 return;
29259             }
29260             if(ddays){
29261                 if(ddays.indexOf(d.getDay()) != -1){
29262                     cell.title = ddaysText;
29263                     cell.className = " x-date-disabled";
29264                 }
29265             }
29266             if(ddMatch && format){
29267                 var fvalue = d.dateFormat(format);
29268                 if(ddMatch.test(fvalue)){
29269                     cell.title = ddText.replace("%0", fvalue);
29270                     cell.className = " x-date-disabled";
29271                 }
29272             }
29273         };
29274
29275         var i = 0;
29276         for(; i < startingPos; i++) {
29277             textEls[i].innerHTML = (++prevStart);
29278             d.setDate(d.getDate()+1);
29279             cells[i].className = "x-date-prevday";
29280             setCellClass(this, cells[i]);
29281         }
29282         for(; i < days; i++){
29283             intDay = i - startingPos + 1;
29284             textEls[i].innerHTML = (intDay);
29285             d.setDate(d.getDate()+1);
29286             cells[i].className = "x-date-active";
29287             setCellClass(this, cells[i]);
29288         }
29289         var extraDays = 0;
29290         for(; i < 42; i++) {
29291              textEls[i].innerHTML = (++extraDays);
29292              d.setDate(d.getDate()+1);
29293              cells[i].className = "x-date-nextday";
29294              setCellClass(this, cells[i]);
29295         }
29296
29297         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29298         this.fireEvent('monthchange', this, date);
29299         
29300         if(!this.internalRender){
29301             var main = this.el.dom.firstChild;
29302             var w = main.offsetWidth;
29303             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29304             Roo.fly(main).setWidth(w);
29305             this.internalRender = true;
29306             // opera does not respect the auto grow header center column
29307             // then, after it gets a width opera refuses to recalculate
29308             // without a second pass
29309             if(Roo.isOpera && !this.secondPass){
29310                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29311                 this.secondPass = true;
29312                 this.update.defer(10, this, [date]);
29313             }
29314         }
29315         
29316         
29317     }
29318 });        /*
29319  * Based on:
29320  * Ext JS Library 1.1.1
29321  * Copyright(c) 2006-2007, Ext JS, LLC.
29322  *
29323  * Originally Released Under LGPL - original licence link has changed is not relivant.
29324  *
29325  * Fork - LGPL
29326  * <script type="text/javascript">
29327  */
29328 /**
29329  * @class Roo.TabPanel
29330  * @extends Roo.util.Observable
29331  * A lightweight tab container.
29332  * <br><br>
29333  * Usage:
29334  * <pre><code>
29335 // basic tabs 1, built from existing content
29336 var tabs = new Roo.TabPanel("tabs1");
29337 tabs.addTab("script", "View Script");
29338 tabs.addTab("markup", "View Markup");
29339 tabs.activate("script");
29340
29341 // more advanced tabs, built from javascript
29342 var jtabs = new Roo.TabPanel("jtabs");
29343 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29344
29345 // set up the UpdateManager
29346 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29347 var updater = tab2.getUpdateManager();
29348 updater.setDefaultUrl("ajax1.htm");
29349 tab2.on('activate', updater.refresh, updater, true);
29350
29351 // Use setUrl for Ajax loading
29352 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29353 tab3.setUrl("ajax2.htm", null, true);
29354
29355 // Disabled tab
29356 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29357 tab4.disable();
29358
29359 jtabs.activate("jtabs-1");
29360  * </code></pre>
29361  * @constructor
29362  * Create a new TabPanel.
29363  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29364  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29365  */
29366 Roo.TabPanel = function(container, config){
29367     /**
29368     * The container element for this TabPanel.
29369     * @type Roo.Element
29370     */
29371     this.el = Roo.get(container, true);
29372     if(config){
29373         if(typeof config == "boolean"){
29374             this.tabPosition = config ? "bottom" : "top";
29375         }else{
29376             Roo.apply(this, config);
29377         }
29378     }
29379     if(this.tabPosition == "bottom"){
29380         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29381         this.el.addClass("x-tabs-bottom");
29382     }
29383     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29384     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29385     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29386     if(Roo.isIE){
29387         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29388     }
29389     if(this.tabPosition != "bottom"){
29390         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29391          * @type Roo.Element
29392          */
29393         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29394         this.el.addClass("x-tabs-top");
29395     }
29396     this.items = [];
29397
29398     this.bodyEl.setStyle("position", "relative");
29399
29400     this.active = null;
29401     this.activateDelegate = this.activate.createDelegate(this);
29402
29403     this.addEvents({
29404         /**
29405          * @event tabchange
29406          * Fires when the active tab changes
29407          * @param {Roo.TabPanel} this
29408          * @param {Roo.TabPanelItem} activePanel The new active tab
29409          */
29410         "tabchange": true,
29411         /**
29412          * @event beforetabchange
29413          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29414          * @param {Roo.TabPanel} this
29415          * @param {Object} e Set cancel to true on this object to cancel the tab change
29416          * @param {Roo.TabPanelItem} tab The tab being changed to
29417          */
29418         "beforetabchange" : true
29419     });
29420
29421     Roo.EventManager.onWindowResize(this.onResize, this);
29422     this.cpad = this.el.getPadding("lr");
29423     this.hiddenCount = 0;
29424
29425
29426     // toolbar on the tabbar support...
29427     if (this.toolbar) {
29428         var tcfg = this.toolbar;
29429         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29430         this.toolbar = new Roo.Toolbar(tcfg);
29431         if (Roo.isSafari) {
29432             var tbl = tcfg.container.child('table', true);
29433             tbl.setAttribute('width', '100%');
29434         }
29435         
29436     }
29437    
29438
29439
29440     Roo.TabPanel.superclass.constructor.call(this);
29441 };
29442
29443 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29444     /*
29445      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29446      */
29447     tabPosition : "top",
29448     /*
29449      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29450      */
29451     currentTabWidth : 0,
29452     /*
29453      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29454      */
29455     minTabWidth : 40,
29456     /*
29457      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29458      */
29459     maxTabWidth : 250,
29460     /*
29461      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29462      */
29463     preferredTabWidth : 175,
29464     /*
29465      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29466      */
29467     resizeTabs : false,
29468     /*
29469      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29470      */
29471     monitorResize : true,
29472     /*
29473      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29474      */
29475     toolbar : false,
29476
29477     /**
29478      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29479      * @param {String} id The id of the div to use <b>or create</b>
29480      * @param {String} text The text for the tab
29481      * @param {String} content (optional) Content to put in the TabPanelItem body
29482      * @param {Boolean} closable (optional) True to create a close icon on the tab
29483      * @return {Roo.TabPanelItem} The created TabPanelItem
29484      */
29485     addTab : function(id, text, content, closable){
29486         var item = new Roo.TabPanelItem(this, id, text, closable);
29487         this.addTabItem(item);
29488         if(content){
29489             item.setContent(content);
29490         }
29491         return item;
29492     },
29493
29494     /**
29495      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29496      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29497      * @return {Roo.TabPanelItem}
29498      */
29499     getTab : function(id){
29500         return this.items[id];
29501     },
29502
29503     /**
29504      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29505      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29506      */
29507     hideTab : function(id){
29508         var t = this.items[id];
29509         if(!t.isHidden()){
29510            t.setHidden(true);
29511            this.hiddenCount++;
29512            this.autoSizeTabs();
29513         }
29514     },
29515
29516     /**
29517      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29518      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29519      */
29520     unhideTab : function(id){
29521         var t = this.items[id];
29522         if(t.isHidden()){
29523            t.setHidden(false);
29524            this.hiddenCount--;
29525            this.autoSizeTabs();
29526         }
29527     },
29528
29529     /**
29530      * Adds an existing {@link Roo.TabPanelItem}.
29531      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29532      */
29533     addTabItem : function(item){
29534         this.items[item.id] = item;
29535         this.items.push(item);
29536         if(this.resizeTabs){
29537            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29538            this.autoSizeTabs();
29539         }else{
29540             item.autoSize();
29541         }
29542     },
29543
29544     /**
29545      * Removes a {@link Roo.TabPanelItem}.
29546      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29547      */
29548     removeTab : function(id){
29549         var items = this.items;
29550         var tab = items[id];
29551         if(!tab) { return; }
29552         var index = items.indexOf(tab);
29553         if(this.active == tab && items.length > 1){
29554             var newTab = this.getNextAvailable(index);
29555             if(newTab) {
29556                 newTab.activate();
29557             }
29558         }
29559         this.stripEl.dom.removeChild(tab.pnode.dom);
29560         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29561             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29562         }
29563         items.splice(index, 1);
29564         delete this.items[tab.id];
29565         tab.fireEvent("close", tab);
29566         tab.purgeListeners();
29567         this.autoSizeTabs();
29568     },
29569
29570     getNextAvailable : function(start){
29571         var items = this.items;
29572         var index = start;
29573         // look for a next tab that will slide over to
29574         // replace the one being removed
29575         while(index < items.length){
29576             var item = items[++index];
29577             if(item && !item.isHidden()){
29578                 return item;
29579             }
29580         }
29581         // if one isn't found select the previous tab (on the left)
29582         index = start;
29583         while(index >= 0){
29584             var item = items[--index];
29585             if(item && !item.isHidden()){
29586                 return item;
29587             }
29588         }
29589         return null;
29590     },
29591
29592     /**
29593      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29594      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29595      */
29596     disableTab : function(id){
29597         var tab = this.items[id];
29598         if(tab && this.active != tab){
29599             tab.disable();
29600         }
29601     },
29602
29603     /**
29604      * Enables a {@link Roo.TabPanelItem} that is disabled.
29605      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29606      */
29607     enableTab : function(id){
29608         var tab = this.items[id];
29609         tab.enable();
29610     },
29611
29612     /**
29613      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29614      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29615      * @return {Roo.TabPanelItem} The TabPanelItem.
29616      */
29617     activate : function(id){
29618         var tab = this.items[id];
29619         if(!tab){
29620             return null;
29621         }
29622         if(tab == this.active || tab.disabled){
29623             return tab;
29624         }
29625         var e = {};
29626         this.fireEvent("beforetabchange", this, e, tab);
29627         if(e.cancel !== true && !tab.disabled){
29628             if(this.active){
29629                 this.active.hide();
29630             }
29631             this.active = this.items[id];
29632             this.active.show();
29633             this.fireEvent("tabchange", this, this.active);
29634         }
29635         return tab;
29636     },
29637
29638     /**
29639      * Gets the active {@link Roo.TabPanelItem}.
29640      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29641      */
29642     getActiveTab : function(){
29643         return this.active;
29644     },
29645
29646     /**
29647      * Updates the tab body element to fit the height of the container element
29648      * for overflow scrolling
29649      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29650      */
29651     syncHeight : function(targetHeight){
29652         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29653         var bm = this.bodyEl.getMargins();
29654         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29655         this.bodyEl.setHeight(newHeight);
29656         return newHeight;
29657     },
29658
29659     onResize : function(){
29660         if(this.monitorResize){
29661             this.autoSizeTabs();
29662         }
29663     },
29664
29665     /**
29666      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29667      */
29668     beginUpdate : function(){
29669         this.updating = true;
29670     },
29671
29672     /**
29673      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29674      */
29675     endUpdate : function(){
29676         this.updating = false;
29677         this.autoSizeTabs();
29678     },
29679
29680     /**
29681      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29682      */
29683     autoSizeTabs : function(){
29684         var count = this.items.length;
29685         var vcount = count - this.hiddenCount;
29686         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29687             return;
29688         }
29689         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29690         var availWidth = Math.floor(w / vcount);
29691         var b = this.stripBody;
29692         if(b.getWidth() > w){
29693             var tabs = this.items;
29694             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29695             if(availWidth < this.minTabWidth){
29696                 /*if(!this.sleft){    // incomplete scrolling code
29697                     this.createScrollButtons();
29698                 }
29699                 this.showScroll();
29700                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29701             }
29702         }else{
29703             if(this.currentTabWidth < this.preferredTabWidth){
29704                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29705             }
29706         }
29707     },
29708
29709     /**
29710      * Returns the number of tabs in this TabPanel.
29711      * @return {Number}
29712      */
29713      getCount : function(){
29714          return this.items.length;
29715      },
29716
29717     /**
29718      * Resizes all the tabs to the passed width
29719      * @param {Number} The new width
29720      */
29721     setTabWidth : function(width){
29722         this.currentTabWidth = width;
29723         for(var i = 0, len = this.items.length; i < len; i++) {
29724                 if(!this.items[i].isHidden()) {
29725                 this.items[i].setWidth(width);
29726             }
29727         }
29728     },
29729
29730     /**
29731      * Destroys this TabPanel
29732      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29733      */
29734     destroy : function(removeEl){
29735         Roo.EventManager.removeResizeListener(this.onResize, this);
29736         for(var i = 0, len = this.items.length; i < len; i++){
29737             this.items[i].purgeListeners();
29738         }
29739         if(removeEl === true){
29740             this.el.update("");
29741             this.el.remove();
29742         }
29743     }
29744 });
29745
29746 /**
29747  * @class Roo.TabPanelItem
29748  * @extends Roo.util.Observable
29749  * Represents an individual item (tab plus body) in a TabPanel.
29750  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29751  * @param {String} id The id of this TabPanelItem
29752  * @param {String} text The text for the tab of this TabPanelItem
29753  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29754  */
29755 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29756     /**
29757      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29758      * @type Roo.TabPanel
29759      */
29760     this.tabPanel = tabPanel;
29761     /**
29762      * The id for this TabPanelItem
29763      * @type String
29764      */
29765     this.id = id;
29766     /** @private */
29767     this.disabled = false;
29768     /** @private */
29769     this.text = text;
29770     /** @private */
29771     this.loaded = false;
29772     this.closable = closable;
29773
29774     /**
29775      * The body element for this TabPanelItem.
29776      * @type Roo.Element
29777      */
29778     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29779     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29780     this.bodyEl.setStyle("display", "block");
29781     this.bodyEl.setStyle("zoom", "1");
29782     this.hideAction();
29783
29784     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29785     /** @private */
29786     this.el = Roo.get(els.el, true);
29787     this.inner = Roo.get(els.inner, true);
29788     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29789     this.pnode = Roo.get(els.el.parentNode, true);
29790     this.el.on("mousedown", this.onTabMouseDown, this);
29791     this.el.on("click", this.onTabClick, this);
29792     /** @private */
29793     if(closable){
29794         var c = Roo.get(els.close, true);
29795         c.dom.title = this.closeText;
29796         c.addClassOnOver("close-over");
29797         c.on("click", this.closeClick, this);
29798      }
29799
29800     this.addEvents({
29801          /**
29802          * @event activate
29803          * Fires when this tab becomes the active tab.
29804          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29805          * @param {Roo.TabPanelItem} this
29806          */
29807         "activate": true,
29808         /**
29809          * @event beforeclose
29810          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29811          * @param {Roo.TabPanelItem} this
29812          * @param {Object} e Set cancel to true on this object to cancel the close.
29813          */
29814         "beforeclose": true,
29815         /**
29816          * @event close
29817          * Fires when this tab is closed.
29818          * @param {Roo.TabPanelItem} this
29819          */
29820          "close": true,
29821         /**
29822          * @event deactivate
29823          * Fires when this tab is no longer the active tab.
29824          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29825          * @param {Roo.TabPanelItem} this
29826          */
29827          "deactivate" : true
29828     });
29829     this.hidden = false;
29830
29831     Roo.TabPanelItem.superclass.constructor.call(this);
29832 };
29833
29834 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29835     purgeListeners : function(){
29836        Roo.util.Observable.prototype.purgeListeners.call(this);
29837        this.el.removeAllListeners();
29838     },
29839     /**
29840      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29841      */
29842     show : function(){
29843         this.pnode.addClass("on");
29844         this.showAction();
29845         if(Roo.isOpera){
29846             this.tabPanel.stripWrap.repaint();
29847         }
29848         this.fireEvent("activate", this.tabPanel, this);
29849     },
29850
29851     /**
29852      * Returns true if this tab is the active tab.
29853      * @return {Boolean}
29854      */
29855     isActive : function(){
29856         return this.tabPanel.getActiveTab() == this;
29857     },
29858
29859     /**
29860      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29861      */
29862     hide : function(){
29863         this.pnode.removeClass("on");
29864         this.hideAction();
29865         this.fireEvent("deactivate", this.tabPanel, this);
29866     },
29867
29868     hideAction : function(){
29869         this.bodyEl.hide();
29870         this.bodyEl.setStyle("position", "absolute");
29871         this.bodyEl.setLeft("-20000px");
29872         this.bodyEl.setTop("-20000px");
29873     },
29874
29875     showAction : function(){
29876         this.bodyEl.setStyle("position", "relative");
29877         this.bodyEl.setTop("");
29878         this.bodyEl.setLeft("");
29879         this.bodyEl.show();
29880     },
29881
29882     /**
29883      * Set the tooltip for the tab.
29884      * @param {String} tooltip The tab's tooltip
29885      */
29886     setTooltip : function(text){
29887         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29888             this.textEl.dom.qtip = text;
29889             this.textEl.dom.removeAttribute('title');
29890         }else{
29891             this.textEl.dom.title = text;
29892         }
29893     },
29894
29895     onTabClick : function(e){
29896         e.preventDefault();
29897         this.tabPanel.activate(this.id);
29898     },
29899
29900     onTabMouseDown : function(e){
29901         e.preventDefault();
29902         this.tabPanel.activate(this.id);
29903     },
29904
29905     getWidth : function(){
29906         return this.inner.getWidth();
29907     },
29908
29909     setWidth : function(width){
29910         var iwidth = width - this.pnode.getPadding("lr");
29911         this.inner.setWidth(iwidth);
29912         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29913         this.pnode.setWidth(width);
29914     },
29915
29916     /**
29917      * Show or hide the tab
29918      * @param {Boolean} hidden True to hide or false to show.
29919      */
29920     setHidden : function(hidden){
29921         this.hidden = hidden;
29922         this.pnode.setStyle("display", hidden ? "none" : "");
29923     },
29924
29925     /**
29926      * Returns true if this tab is "hidden"
29927      * @return {Boolean}
29928      */
29929     isHidden : function(){
29930         return this.hidden;
29931     },
29932
29933     /**
29934      * Returns the text for this tab
29935      * @return {String}
29936      */
29937     getText : function(){
29938         return this.text;
29939     },
29940
29941     autoSize : function(){
29942         //this.el.beginMeasure();
29943         this.textEl.setWidth(1);
29944         /*
29945          *  #2804 [new] Tabs in Roojs
29946          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29947          */
29948         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29949         //this.el.endMeasure();
29950     },
29951
29952     /**
29953      * Sets the text for the tab (Note: this also sets the tooltip text)
29954      * @param {String} text The tab's text and tooltip
29955      */
29956     setText : function(text){
29957         this.text = text;
29958         this.textEl.update(text);
29959         this.setTooltip(text);
29960         if(!this.tabPanel.resizeTabs){
29961             this.autoSize();
29962         }
29963     },
29964     /**
29965      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29966      */
29967     activate : function(){
29968         this.tabPanel.activate(this.id);
29969     },
29970
29971     /**
29972      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29973      */
29974     disable : function(){
29975         if(this.tabPanel.active != this){
29976             this.disabled = true;
29977             this.pnode.addClass("disabled");
29978         }
29979     },
29980
29981     /**
29982      * Enables this TabPanelItem if it was previously disabled.
29983      */
29984     enable : function(){
29985         this.disabled = false;
29986         this.pnode.removeClass("disabled");
29987     },
29988
29989     /**
29990      * Sets the content for this TabPanelItem.
29991      * @param {String} content The content
29992      * @param {Boolean} loadScripts true to look for and load scripts
29993      */
29994     setContent : function(content, loadScripts){
29995         this.bodyEl.update(content, loadScripts);
29996     },
29997
29998     /**
29999      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30000      * @return {Roo.UpdateManager} The UpdateManager
30001      */
30002     getUpdateManager : function(){
30003         return this.bodyEl.getUpdateManager();
30004     },
30005
30006     /**
30007      * Set a URL to be used to load the content for this TabPanelItem.
30008      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30009      * @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)
30010      * @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)
30011      * @return {Roo.UpdateManager} The UpdateManager
30012      */
30013     setUrl : function(url, params, loadOnce){
30014         if(this.refreshDelegate){
30015             this.un('activate', this.refreshDelegate);
30016         }
30017         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30018         this.on("activate", this.refreshDelegate);
30019         return this.bodyEl.getUpdateManager();
30020     },
30021
30022     /** @private */
30023     _handleRefresh : function(url, params, loadOnce){
30024         if(!loadOnce || !this.loaded){
30025             var updater = this.bodyEl.getUpdateManager();
30026             updater.update(url, params, this._setLoaded.createDelegate(this));
30027         }
30028     },
30029
30030     /**
30031      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30032      *   Will fail silently if the setUrl method has not been called.
30033      *   This does not activate the panel, just updates its content.
30034      */
30035     refresh : function(){
30036         if(this.refreshDelegate){
30037            this.loaded = false;
30038            this.refreshDelegate();
30039         }
30040     },
30041
30042     /** @private */
30043     _setLoaded : function(){
30044         this.loaded = true;
30045     },
30046
30047     /** @private */
30048     closeClick : function(e){
30049         var o = {};
30050         e.stopEvent();
30051         this.fireEvent("beforeclose", this, o);
30052         if(o.cancel !== true){
30053             this.tabPanel.removeTab(this.id);
30054         }
30055     },
30056     /**
30057      * The text displayed in the tooltip for the close icon.
30058      * @type String
30059      */
30060     closeText : "Close this tab"
30061 });
30062
30063 /** @private */
30064 Roo.TabPanel.prototype.createStrip = function(container){
30065     var strip = document.createElement("div");
30066     strip.className = "x-tabs-wrap";
30067     container.appendChild(strip);
30068     return strip;
30069 };
30070 /** @private */
30071 Roo.TabPanel.prototype.createStripList = function(strip){
30072     // div wrapper for retard IE
30073     // returns the "tr" element.
30074     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30075         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30076         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30077     return strip.firstChild.firstChild.firstChild.firstChild;
30078 };
30079 /** @private */
30080 Roo.TabPanel.prototype.createBody = function(container){
30081     var body = document.createElement("div");
30082     Roo.id(body, "tab-body");
30083     Roo.fly(body).addClass("x-tabs-body");
30084     container.appendChild(body);
30085     return body;
30086 };
30087 /** @private */
30088 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30089     var body = Roo.getDom(id);
30090     if(!body){
30091         body = document.createElement("div");
30092         body.id = id;
30093     }
30094     Roo.fly(body).addClass("x-tabs-item-body");
30095     bodyEl.insertBefore(body, bodyEl.firstChild);
30096     return body;
30097 };
30098 /** @private */
30099 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30100     var td = document.createElement("td");
30101     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30102     //stripEl.appendChild(td);
30103     if(closable){
30104         td.className = "x-tabs-closable";
30105         if(!this.closeTpl){
30106             this.closeTpl = new Roo.Template(
30107                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30108                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30109                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30110             );
30111         }
30112         var el = this.closeTpl.overwrite(td, {"text": text});
30113         var close = el.getElementsByTagName("div")[0];
30114         var inner = el.getElementsByTagName("em")[0];
30115         return {"el": el, "close": close, "inner": inner};
30116     } else {
30117         if(!this.tabTpl){
30118             this.tabTpl = new Roo.Template(
30119                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30120                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30121             );
30122         }
30123         var el = this.tabTpl.overwrite(td, {"text": text});
30124         var inner = el.getElementsByTagName("em")[0];
30125         return {"el": el, "inner": inner};
30126     }
30127 };/*
30128  * Based on:
30129  * Ext JS Library 1.1.1
30130  * Copyright(c) 2006-2007, Ext JS, LLC.
30131  *
30132  * Originally Released Under LGPL - original licence link has changed is not relivant.
30133  *
30134  * Fork - LGPL
30135  * <script type="text/javascript">
30136  */
30137
30138 /**
30139  * @class Roo.Button
30140  * @extends Roo.util.Observable
30141  * Simple Button class
30142  * @cfg {String} text The button text
30143  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30144  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30145  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30146  * @cfg {Object} scope The scope of the handler
30147  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30148  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30149  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30150  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30151  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30152  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30153    applies if enableToggle = true)
30154  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30155  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30156   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30157  * @constructor
30158  * Create a new button
30159  * @param {Object} config The config object
30160  */
30161 Roo.Button = function(renderTo, config)
30162 {
30163     if (!config) {
30164         config = renderTo;
30165         renderTo = config.renderTo || false;
30166     }
30167     
30168     Roo.apply(this, config);
30169     this.addEvents({
30170         /**
30171              * @event click
30172              * Fires when this button is clicked
30173              * @param {Button} this
30174              * @param {EventObject} e The click event
30175              */
30176             "click" : true,
30177         /**
30178              * @event toggle
30179              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30180              * @param {Button} this
30181              * @param {Boolean} pressed
30182              */
30183             "toggle" : true,
30184         /**
30185              * @event mouseover
30186              * Fires when the mouse hovers over the button
30187              * @param {Button} this
30188              * @param {Event} e The event object
30189              */
30190         'mouseover' : true,
30191         /**
30192              * @event mouseout
30193              * Fires when the mouse exits the button
30194              * @param {Button} this
30195              * @param {Event} e The event object
30196              */
30197         'mouseout': true,
30198          /**
30199              * @event render
30200              * Fires when the button is rendered
30201              * @param {Button} this
30202              */
30203         'render': true
30204     });
30205     if(this.menu){
30206         this.menu = Roo.menu.MenuMgr.get(this.menu);
30207     }
30208     // register listeners first!!  - so render can be captured..
30209     Roo.util.Observable.call(this);
30210     if(renderTo){
30211         this.render(renderTo);
30212     }
30213     
30214   
30215 };
30216
30217 Roo.extend(Roo.Button, Roo.util.Observable, {
30218     /**
30219      * 
30220      */
30221     
30222     /**
30223      * Read-only. True if this button is hidden
30224      * @type Boolean
30225      */
30226     hidden : false,
30227     /**
30228      * Read-only. True if this button is disabled
30229      * @type Boolean
30230      */
30231     disabled : false,
30232     /**
30233      * Read-only. True if this button is pressed (only if enableToggle = true)
30234      * @type Boolean
30235      */
30236     pressed : false,
30237
30238     /**
30239      * @cfg {Number} tabIndex 
30240      * The DOM tabIndex for this button (defaults to undefined)
30241      */
30242     tabIndex : undefined,
30243
30244     /**
30245      * @cfg {Boolean} enableToggle
30246      * True to enable pressed/not pressed toggling (defaults to false)
30247      */
30248     enableToggle: false,
30249     /**
30250      * @cfg {Roo.menu.Menu} menu
30251      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30252      */
30253     menu : undefined,
30254     /**
30255      * @cfg {String} menuAlign
30256      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30257      */
30258     menuAlign : "tl-bl?",
30259
30260     /**
30261      * @cfg {String} iconCls
30262      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30263      */
30264     iconCls : undefined,
30265     /**
30266      * @cfg {String} type
30267      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30268      */
30269     type : 'button',
30270
30271     // private
30272     menuClassTarget: 'tr',
30273
30274     /**
30275      * @cfg {String} clickEvent
30276      * The type of event to map to the button's event handler (defaults to 'click')
30277      */
30278     clickEvent : 'click',
30279
30280     /**
30281      * @cfg {Boolean} handleMouseEvents
30282      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30283      */
30284     handleMouseEvents : true,
30285
30286     /**
30287      * @cfg {String} tooltipType
30288      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30289      */
30290     tooltipType : 'qtip',
30291
30292     /**
30293      * @cfg {String} cls
30294      * A CSS class to apply to the button's main element.
30295      */
30296     
30297     /**
30298      * @cfg {Roo.Template} template (Optional)
30299      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30300      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30301      * require code modifications if required elements (e.g. a button) aren't present.
30302      */
30303
30304     // private
30305     render : function(renderTo){
30306         var btn;
30307         if(this.hideParent){
30308             this.parentEl = Roo.get(renderTo);
30309         }
30310         if(!this.dhconfig){
30311             if(!this.template){
30312                 if(!Roo.Button.buttonTemplate){
30313                     // hideous table template
30314                     Roo.Button.buttonTemplate = new Roo.Template(
30315                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30316                         '<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>',
30317                         "</tr></tbody></table>");
30318                 }
30319                 this.template = Roo.Button.buttonTemplate;
30320             }
30321             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30322             var btnEl = btn.child("button:first");
30323             btnEl.on('focus', this.onFocus, this);
30324             btnEl.on('blur', this.onBlur, this);
30325             if(this.cls){
30326                 btn.addClass(this.cls);
30327             }
30328             if(this.icon){
30329                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30330             }
30331             if(this.iconCls){
30332                 btnEl.addClass(this.iconCls);
30333                 if(!this.cls){
30334                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30335                 }
30336             }
30337             if(this.tabIndex !== undefined){
30338                 btnEl.dom.tabIndex = this.tabIndex;
30339             }
30340             if(this.tooltip){
30341                 if(typeof this.tooltip == 'object'){
30342                     Roo.QuickTips.tips(Roo.apply({
30343                           target: btnEl.id
30344                     }, this.tooltip));
30345                 } else {
30346                     btnEl.dom[this.tooltipType] = this.tooltip;
30347                 }
30348             }
30349         }else{
30350             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30351         }
30352         this.el = btn;
30353         if(this.id){
30354             this.el.dom.id = this.el.id = this.id;
30355         }
30356         if(this.menu){
30357             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30358             this.menu.on("show", this.onMenuShow, this);
30359             this.menu.on("hide", this.onMenuHide, this);
30360         }
30361         btn.addClass("x-btn");
30362         if(Roo.isIE && !Roo.isIE7){
30363             this.autoWidth.defer(1, this);
30364         }else{
30365             this.autoWidth();
30366         }
30367         if(this.handleMouseEvents){
30368             btn.on("mouseover", this.onMouseOver, this);
30369             btn.on("mouseout", this.onMouseOut, this);
30370             btn.on("mousedown", this.onMouseDown, this);
30371         }
30372         btn.on(this.clickEvent, this.onClick, this);
30373         //btn.on("mouseup", this.onMouseUp, this);
30374         if(this.hidden){
30375             this.hide();
30376         }
30377         if(this.disabled){
30378             this.disable();
30379         }
30380         Roo.ButtonToggleMgr.register(this);
30381         if(this.pressed){
30382             this.el.addClass("x-btn-pressed");
30383         }
30384         if(this.repeat){
30385             var repeater = new Roo.util.ClickRepeater(btn,
30386                 typeof this.repeat == "object" ? this.repeat : {}
30387             );
30388             repeater.on("click", this.onClick,  this);
30389         }
30390         
30391         this.fireEvent('render', this);
30392         
30393     },
30394     /**
30395      * Returns the button's underlying element
30396      * @return {Roo.Element} The element
30397      */
30398     getEl : function(){
30399         return this.el;  
30400     },
30401     
30402     /**
30403      * Destroys this Button and removes any listeners.
30404      */
30405     destroy : function(){
30406         Roo.ButtonToggleMgr.unregister(this);
30407         this.el.removeAllListeners();
30408         this.purgeListeners();
30409         this.el.remove();
30410     },
30411
30412     // private
30413     autoWidth : function(){
30414         if(this.el){
30415             this.el.setWidth("auto");
30416             if(Roo.isIE7 && Roo.isStrict){
30417                 var ib = this.el.child('button');
30418                 if(ib && ib.getWidth() > 20){
30419                     ib.clip();
30420                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30421                 }
30422             }
30423             if(this.minWidth){
30424                 if(this.hidden){
30425                     this.el.beginMeasure();
30426                 }
30427                 if(this.el.getWidth() < this.minWidth){
30428                     this.el.setWidth(this.minWidth);
30429                 }
30430                 if(this.hidden){
30431                     this.el.endMeasure();
30432                 }
30433             }
30434         }
30435     },
30436
30437     /**
30438      * Assigns this button's click handler
30439      * @param {Function} handler The function to call when the button is clicked
30440      * @param {Object} scope (optional) Scope for the function passed in
30441      */
30442     setHandler : function(handler, scope){
30443         this.handler = handler;
30444         this.scope = scope;  
30445     },
30446     
30447     /**
30448      * Sets this button's text
30449      * @param {String} text The button text
30450      */
30451     setText : function(text){
30452         this.text = text;
30453         if(this.el){
30454             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30455         }
30456         this.autoWidth();
30457     },
30458     
30459     /**
30460      * Gets the text for this button
30461      * @return {String} The button text
30462      */
30463     getText : function(){
30464         return this.text;  
30465     },
30466     
30467     /**
30468      * Show this button
30469      */
30470     show: function(){
30471         this.hidden = false;
30472         if(this.el){
30473             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30474         }
30475     },
30476     
30477     /**
30478      * Hide this button
30479      */
30480     hide: function(){
30481         this.hidden = true;
30482         if(this.el){
30483             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30484         }
30485     },
30486     
30487     /**
30488      * Convenience function for boolean show/hide
30489      * @param {Boolean} visible True to show, false to hide
30490      */
30491     setVisible: function(visible){
30492         if(visible) {
30493             this.show();
30494         }else{
30495             this.hide();
30496         }
30497     },
30498     
30499     /**
30500      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30501      * @param {Boolean} state (optional) Force a particular state
30502      */
30503     toggle : function(state){
30504         state = state === undefined ? !this.pressed : state;
30505         if(state != this.pressed){
30506             if(state){
30507                 this.el.addClass("x-btn-pressed");
30508                 this.pressed = true;
30509                 this.fireEvent("toggle", this, true);
30510             }else{
30511                 this.el.removeClass("x-btn-pressed");
30512                 this.pressed = false;
30513                 this.fireEvent("toggle", this, false);
30514             }
30515             if(this.toggleHandler){
30516                 this.toggleHandler.call(this.scope || this, this, state);
30517             }
30518         }
30519     },
30520     
30521     /**
30522      * Focus the button
30523      */
30524     focus : function(){
30525         this.el.child('button:first').focus();
30526     },
30527     
30528     /**
30529      * Disable this button
30530      */
30531     disable : function(){
30532         if(this.el){
30533             this.el.addClass("x-btn-disabled");
30534         }
30535         this.disabled = true;
30536     },
30537     
30538     /**
30539      * Enable this button
30540      */
30541     enable : function(){
30542         if(this.el){
30543             this.el.removeClass("x-btn-disabled");
30544         }
30545         this.disabled = false;
30546     },
30547
30548     /**
30549      * Convenience function for boolean enable/disable
30550      * @param {Boolean} enabled True to enable, false to disable
30551      */
30552     setDisabled : function(v){
30553         this[v !== true ? "enable" : "disable"]();
30554     },
30555
30556     // private
30557     onClick : function(e)
30558     {
30559         if(e){
30560             e.preventDefault();
30561         }
30562         if(e.button != 0){
30563             return;
30564         }
30565         if(!this.disabled){
30566             if(this.enableToggle){
30567                 this.toggle();
30568             }
30569             if(this.menu && !this.menu.isVisible()){
30570                 this.menu.show(this.el, this.menuAlign);
30571             }
30572             this.fireEvent("click", this, e);
30573             if(this.handler){
30574                 this.el.removeClass("x-btn-over");
30575                 this.handler.call(this.scope || this, this, e);
30576             }
30577         }
30578     },
30579     // private
30580     onMouseOver : function(e){
30581         if(!this.disabled){
30582             this.el.addClass("x-btn-over");
30583             this.fireEvent('mouseover', this, e);
30584         }
30585     },
30586     // private
30587     onMouseOut : function(e){
30588         if(!e.within(this.el,  true)){
30589             this.el.removeClass("x-btn-over");
30590             this.fireEvent('mouseout', this, e);
30591         }
30592     },
30593     // private
30594     onFocus : function(e){
30595         if(!this.disabled){
30596             this.el.addClass("x-btn-focus");
30597         }
30598     },
30599     // private
30600     onBlur : function(e){
30601         this.el.removeClass("x-btn-focus");
30602     },
30603     // private
30604     onMouseDown : function(e){
30605         if(!this.disabled && e.button == 0){
30606             this.el.addClass("x-btn-click");
30607             Roo.get(document).on('mouseup', this.onMouseUp, this);
30608         }
30609     },
30610     // private
30611     onMouseUp : function(e){
30612         if(e.button == 0){
30613             this.el.removeClass("x-btn-click");
30614             Roo.get(document).un('mouseup', this.onMouseUp, this);
30615         }
30616     },
30617     // private
30618     onMenuShow : function(e){
30619         this.el.addClass("x-btn-menu-active");
30620     },
30621     // private
30622     onMenuHide : function(e){
30623         this.el.removeClass("x-btn-menu-active");
30624     }   
30625 });
30626
30627 // Private utility class used by Button
30628 Roo.ButtonToggleMgr = function(){
30629    var groups = {};
30630    
30631    function toggleGroup(btn, state){
30632        if(state){
30633            var g = groups[btn.toggleGroup];
30634            for(var i = 0, l = g.length; i < l; i++){
30635                if(g[i] != btn){
30636                    g[i].toggle(false);
30637                }
30638            }
30639        }
30640    }
30641    
30642    return {
30643        register : function(btn){
30644            if(!btn.toggleGroup){
30645                return;
30646            }
30647            var g = groups[btn.toggleGroup];
30648            if(!g){
30649                g = groups[btn.toggleGroup] = [];
30650            }
30651            g.push(btn);
30652            btn.on("toggle", toggleGroup);
30653        },
30654        
30655        unregister : function(btn){
30656            if(!btn.toggleGroup){
30657                return;
30658            }
30659            var g = groups[btn.toggleGroup];
30660            if(g){
30661                g.remove(btn);
30662                btn.un("toggle", toggleGroup);
30663            }
30664        }
30665    };
30666 }();/*
30667  * Based on:
30668  * Ext JS Library 1.1.1
30669  * Copyright(c) 2006-2007, Ext JS, LLC.
30670  *
30671  * Originally Released Under LGPL - original licence link has changed is not relivant.
30672  *
30673  * Fork - LGPL
30674  * <script type="text/javascript">
30675  */
30676  
30677 /**
30678  * @class Roo.SplitButton
30679  * @extends Roo.Button
30680  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30681  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30682  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30683  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30684  * @cfg {String} arrowTooltip The title attribute of the arrow
30685  * @constructor
30686  * Create a new menu button
30687  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30688  * @param {Object} config The config object
30689  */
30690 Roo.SplitButton = function(renderTo, config){
30691     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30692     /**
30693      * @event arrowclick
30694      * Fires when this button's arrow is clicked
30695      * @param {SplitButton} this
30696      * @param {EventObject} e The click event
30697      */
30698     this.addEvents({"arrowclick":true});
30699 };
30700
30701 Roo.extend(Roo.SplitButton, Roo.Button, {
30702     render : function(renderTo){
30703         // this is one sweet looking template!
30704         var tpl = new Roo.Template(
30705             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30706             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30707             '<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>',
30708             "</tbody></table></td><td>",
30709             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30710             '<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>',
30711             "</tbody></table></td></tr></table>"
30712         );
30713         var btn = tpl.append(renderTo, [this.text, this.type], true);
30714         var btnEl = btn.child("button");
30715         if(this.cls){
30716             btn.addClass(this.cls);
30717         }
30718         if(this.icon){
30719             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30720         }
30721         if(this.iconCls){
30722             btnEl.addClass(this.iconCls);
30723             if(!this.cls){
30724                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30725             }
30726         }
30727         this.el = btn;
30728         if(this.handleMouseEvents){
30729             btn.on("mouseover", this.onMouseOver, this);
30730             btn.on("mouseout", this.onMouseOut, this);
30731             btn.on("mousedown", this.onMouseDown, this);
30732             btn.on("mouseup", this.onMouseUp, this);
30733         }
30734         btn.on(this.clickEvent, this.onClick, this);
30735         if(this.tooltip){
30736             if(typeof this.tooltip == 'object'){
30737                 Roo.QuickTips.tips(Roo.apply({
30738                       target: btnEl.id
30739                 }, this.tooltip));
30740             } else {
30741                 btnEl.dom[this.tooltipType] = this.tooltip;
30742             }
30743         }
30744         if(this.arrowTooltip){
30745             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30746         }
30747         if(this.hidden){
30748             this.hide();
30749         }
30750         if(this.disabled){
30751             this.disable();
30752         }
30753         if(this.pressed){
30754             this.el.addClass("x-btn-pressed");
30755         }
30756         if(Roo.isIE && !Roo.isIE7){
30757             this.autoWidth.defer(1, this);
30758         }else{
30759             this.autoWidth();
30760         }
30761         if(this.menu){
30762             this.menu.on("show", this.onMenuShow, this);
30763             this.menu.on("hide", this.onMenuHide, this);
30764         }
30765         this.fireEvent('render', this);
30766     },
30767
30768     // private
30769     autoWidth : function(){
30770         if(this.el){
30771             var tbl = this.el.child("table:first");
30772             var tbl2 = this.el.child("table:last");
30773             this.el.setWidth("auto");
30774             tbl.setWidth("auto");
30775             if(Roo.isIE7 && Roo.isStrict){
30776                 var ib = this.el.child('button:first');
30777                 if(ib && ib.getWidth() > 20){
30778                     ib.clip();
30779                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30780                 }
30781             }
30782             if(this.minWidth){
30783                 if(this.hidden){
30784                     this.el.beginMeasure();
30785                 }
30786                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30787                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30788                 }
30789                 if(this.hidden){
30790                     this.el.endMeasure();
30791                 }
30792             }
30793             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30794         } 
30795     },
30796     /**
30797      * Sets this button's click handler
30798      * @param {Function} handler The function to call when the button is clicked
30799      * @param {Object} scope (optional) Scope for the function passed above
30800      */
30801     setHandler : function(handler, scope){
30802         this.handler = handler;
30803         this.scope = scope;  
30804     },
30805     
30806     /**
30807      * Sets this button's arrow click handler
30808      * @param {Function} handler The function to call when the arrow is clicked
30809      * @param {Object} scope (optional) Scope for the function passed above
30810      */
30811     setArrowHandler : function(handler, scope){
30812         this.arrowHandler = handler;
30813         this.scope = scope;  
30814     },
30815     
30816     /**
30817      * Focus the button
30818      */
30819     focus : function(){
30820         if(this.el){
30821             this.el.child("button:first").focus();
30822         }
30823     },
30824
30825     // private
30826     onClick : function(e){
30827         e.preventDefault();
30828         if(!this.disabled){
30829             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30830                 if(this.menu && !this.menu.isVisible()){
30831                     this.menu.show(this.el, this.menuAlign);
30832                 }
30833                 this.fireEvent("arrowclick", this, e);
30834                 if(this.arrowHandler){
30835                     this.arrowHandler.call(this.scope || this, this, e);
30836                 }
30837             }else{
30838                 this.fireEvent("click", this, e);
30839                 if(this.handler){
30840                     this.handler.call(this.scope || this, this, e);
30841                 }
30842             }
30843         }
30844     },
30845     // private
30846     onMouseDown : function(e){
30847         if(!this.disabled){
30848             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30849         }
30850     },
30851     // private
30852     onMouseUp : function(e){
30853         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30854     }   
30855 });
30856
30857
30858 // backwards compat
30859 Roo.MenuButton = Roo.SplitButton;/*
30860  * Based on:
30861  * Ext JS Library 1.1.1
30862  * Copyright(c) 2006-2007, Ext JS, LLC.
30863  *
30864  * Originally Released Under LGPL - original licence link has changed is not relivant.
30865  *
30866  * Fork - LGPL
30867  * <script type="text/javascript">
30868  */
30869
30870 /**
30871  * @class Roo.Toolbar
30872  * @children   Roo.Toolbar.Item Roo.form.Field
30873  * Basic Toolbar class.
30874  * @constructor
30875  * Creates a new Toolbar
30876  * @param {Object} container The config object
30877  */ 
30878 Roo.Toolbar = function(container, buttons, config)
30879 {
30880     /// old consturctor format still supported..
30881     if(container instanceof Array){ // omit the container for later rendering
30882         buttons = container;
30883         config = buttons;
30884         container = null;
30885     }
30886     if (typeof(container) == 'object' && container.xtype) {
30887         config = container;
30888         container = config.container;
30889         buttons = config.buttons || []; // not really - use items!!
30890     }
30891     var xitems = [];
30892     if (config && config.items) {
30893         xitems = config.items;
30894         delete config.items;
30895     }
30896     Roo.apply(this, config);
30897     this.buttons = buttons;
30898     
30899     if(container){
30900         this.render(container);
30901     }
30902     this.xitems = xitems;
30903     Roo.each(xitems, function(b) {
30904         this.add(b);
30905     }, this);
30906     
30907 };
30908
30909 Roo.Toolbar.prototype = {
30910     /**
30911      * @cfg {Array} items
30912      * array of button configs or elements to add (will be converted to a MixedCollection)
30913      */
30914     items: false,
30915     /**
30916      * @cfg {String/HTMLElement/Element} container
30917      * The id or element that will contain the toolbar
30918      */
30919     // private
30920     render : function(ct){
30921         this.el = Roo.get(ct);
30922         if(this.cls){
30923             this.el.addClass(this.cls);
30924         }
30925         // using a table allows for vertical alignment
30926         // 100% width is needed by Safari...
30927         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30928         this.tr = this.el.child("tr", true);
30929         var autoId = 0;
30930         this.items = new Roo.util.MixedCollection(false, function(o){
30931             return o.id || ("item" + (++autoId));
30932         });
30933         if(this.buttons){
30934             this.add.apply(this, this.buttons);
30935             delete this.buttons;
30936         }
30937     },
30938
30939     /**
30940      * Adds element(s) to the toolbar -- this function takes a variable number of 
30941      * arguments of mixed type and adds them to the toolbar.
30942      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30943      * <ul>
30944      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30945      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30946      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30947      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30948      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30949      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30950      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30951      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30952      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30953      * </ul>
30954      * @param {Mixed} arg2
30955      * @param {Mixed} etc.
30956      */
30957     add : function(){
30958         var a = arguments, l = a.length;
30959         for(var i = 0; i < l; i++){
30960             this._add(a[i]);
30961         }
30962     },
30963     // private..
30964     _add : function(el) {
30965         
30966         if (el.xtype) {
30967             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30968         }
30969         
30970         if (el.applyTo){ // some kind of form field
30971             return this.addField(el);
30972         } 
30973         if (el.render){ // some kind of Toolbar.Item
30974             return this.addItem(el);
30975         }
30976         if (typeof el == "string"){ // string
30977             if(el == "separator" || el == "-"){
30978                 return this.addSeparator();
30979             }
30980             if (el == " "){
30981                 return this.addSpacer();
30982             }
30983             if(el == "->"){
30984                 return this.addFill();
30985             }
30986             return this.addText(el);
30987             
30988         }
30989         if(el.tagName){ // element
30990             return this.addElement(el);
30991         }
30992         if(typeof el == "object"){ // must be button config?
30993             return this.addButton(el);
30994         }
30995         // and now what?!?!
30996         return false;
30997         
30998     },
30999     
31000     /**
31001      * Add an Xtype element
31002      * @param {Object} xtype Xtype Object
31003      * @return {Object} created Object
31004      */
31005     addxtype : function(e){
31006         return this.add(e);  
31007     },
31008     
31009     /**
31010      * Returns the Element for this toolbar.
31011      * @return {Roo.Element}
31012      */
31013     getEl : function(){
31014         return this.el;  
31015     },
31016     
31017     /**
31018      * Adds a separator
31019      * @return {Roo.Toolbar.Item} The separator item
31020      */
31021     addSeparator : function(){
31022         return this.addItem(new Roo.Toolbar.Separator());
31023     },
31024
31025     /**
31026      * Adds a spacer element
31027      * @return {Roo.Toolbar.Spacer} The spacer item
31028      */
31029     addSpacer : function(){
31030         return this.addItem(new Roo.Toolbar.Spacer());
31031     },
31032
31033     /**
31034      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31035      * @return {Roo.Toolbar.Fill} The fill item
31036      */
31037     addFill : function(){
31038         return this.addItem(new Roo.Toolbar.Fill());
31039     },
31040
31041     /**
31042      * Adds any standard HTML element to the toolbar
31043      * @param {String/HTMLElement/Element} el The element or id of the element to add
31044      * @return {Roo.Toolbar.Item} The element's item
31045      */
31046     addElement : function(el){
31047         return this.addItem(new Roo.Toolbar.Item(el));
31048     },
31049     /**
31050      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31051      * @type Roo.util.MixedCollection  
31052      */
31053     items : false,
31054      
31055     /**
31056      * Adds any Toolbar.Item or subclass
31057      * @param {Roo.Toolbar.Item} item
31058      * @return {Roo.Toolbar.Item} The item
31059      */
31060     addItem : function(item){
31061         var td = this.nextBlock();
31062         item.render(td);
31063         this.items.add(item);
31064         return item;
31065     },
31066     
31067     /**
31068      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31069      * @param {Object/Array} config A button config or array of configs
31070      * @return {Roo.Toolbar.Button/Array}
31071      */
31072     addButton : function(config){
31073         if(config instanceof Array){
31074             var buttons = [];
31075             for(var i = 0, len = config.length; i < len; i++) {
31076                 buttons.push(this.addButton(config[i]));
31077             }
31078             return buttons;
31079         }
31080         var b = config;
31081         if(!(config instanceof Roo.Toolbar.Button)){
31082             b = config.split ?
31083                 new Roo.Toolbar.SplitButton(config) :
31084                 new Roo.Toolbar.Button(config);
31085         }
31086         var td = this.nextBlock();
31087         b.render(td);
31088         this.items.add(b);
31089         return b;
31090     },
31091     
31092     /**
31093      * Adds text to the toolbar
31094      * @param {String} text The text to add
31095      * @return {Roo.Toolbar.Item} The element's item
31096      */
31097     addText : function(text){
31098         return this.addItem(new Roo.Toolbar.TextItem(text));
31099     },
31100     
31101     /**
31102      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31103      * @param {Number} index The index where the item is to be inserted
31104      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31105      * @return {Roo.Toolbar.Button/Item}
31106      */
31107     insertButton : function(index, item){
31108         if(item instanceof Array){
31109             var buttons = [];
31110             for(var i = 0, len = item.length; i < len; i++) {
31111                buttons.push(this.insertButton(index + i, item[i]));
31112             }
31113             return buttons;
31114         }
31115         if (!(item instanceof Roo.Toolbar.Button)){
31116            item = new Roo.Toolbar.Button(item);
31117         }
31118         var td = document.createElement("td");
31119         this.tr.insertBefore(td, this.tr.childNodes[index]);
31120         item.render(td);
31121         this.items.insert(index, item);
31122         return item;
31123     },
31124     
31125     /**
31126      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31127      * @param {Object} config
31128      * @return {Roo.Toolbar.Item} The element's item
31129      */
31130     addDom : function(config, returnEl){
31131         var td = this.nextBlock();
31132         Roo.DomHelper.overwrite(td, config);
31133         var ti = new Roo.Toolbar.Item(td.firstChild);
31134         ti.render(td);
31135         this.items.add(ti);
31136         return ti;
31137     },
31138
31139     /**
31140      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31141      * @type Roo.util.MixedCollection  
31142      */
31143     fields : false,
31144     
31145     /**
31146      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31147      * Note: the field should not have been rendered yet. For a field that has already been
31148      * rendered, use {@link #addElement}.
31149      * @param {Roo.form.Field} field
31150      * @return {Roo.ToolbarItem}
31151      */
31152      
31153       
31154     addField : function(field) {
31155         if (!this.fields) {
31156             var autoId = 0;
31157             this.fields = new Roo.util.MixedCollection(false, function(o){
31158                 return o.id || ("item" + (++autoId));
31159             });
31160
31161         }
31162         
31163         var td = this.nextBlock();
31164         field.render(td);
31165         var ti = new Roo.Toolbar.Item(td.firstChild);
31166         ti.render(td);
31167         this.items.add(ti);
31168         this.fields.add(field);
31169         return ti;
31170     },
31171     /**
31172      * Hide the toolbar
31173      * @method hide
31174      */
31175      
31176       
31177     hide : function()
31178     {
31179         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31180         this.el.child('div').hide();
31181     },
31182     /**
31183      * Show the toolbar
31184      * @method show
31185      */
31186     show : function()
31187     {
31188         this.el.child('div').show();
31189     },
31190       
31191     // private
31192     nextBlock : function(){
31193         var td = document.createElement("td");
31194         this.tr.appendChild(td);
31195         return td;
31196     },
31197
31198     // private
31199     destroy : function(){
31200         if(this.items){ // rendered?
31201             Roo.destroy.apply(Roo, this.items.items);
31202         }
31203         if(this.fields){ // rendered?
31204             Roo.destroy.apply(Roo, this.fields.items);
31205         }
31206         Roo.Element.uncache(this.el, this.tr);
31207     }
31208 };
31209
31210 /**
31211  * @class Roo.Toolbar.Item
31212  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31213  * @constructor
31214  * Creates a new Item
31215  * @param {HTMLElement} el 
31216  */
31217 Roo.Toolbar.Item = function(el){
31218     var cfg = {};
31219     if (typeof (el.xtype) != 'undefined') {
31220         cfg = el;
31221         el = cfg.el;
31222     }
31223     
31224     this.el = Roo.getDom(el);
31225     this.id = Roo.id(this.el);
31226     this.hidden = false;
31227     
31228     this.addEvents({
31229          /**
31230              * @event render
31231              * Fires when the button is rendered
31232              * @param {Button} this
31233              */
31234         'render': true
31235     });
31236     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31237 };
31238 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31239 //Roo.Toolbar.Item.prototype = {
31240     
31241     /**
31242      * Get this item's HTML Element
31243      * @return {HTMLElement}
31244      */
31245     getEl : function(){
31246        return this.el;  
31247     },
31248
31249     // private
31250     render : function(td){
31251         
31252          this.td = td;
31253         td.appendChild(this.el);
31254         
31255         this.fireEvent('render', this);
31256     },
31257     
31258     /**
31259      * Removes and destroys this item.
31260      */
31261     destroy : function(){
31262         this.td.parentNode.removeChild(this.td);
31263     },
31264     
31265     /**
31266      * Shows this item.
31267      */
31268     show: function(){
31269         this.hidden = false;
31270         this.td.style.display = "";
31271     },
31272     
31273     /**
31274      * Hides this item.
31275      */
31276     hide: function(){
31277         this.hidden = true;
31278         this.td.style.display = "none";
31279     },
31280     
31281     /**
31282      * Convenience function for boolean show/hide.
31283      * @param {Boolean} visible true to show/false to hide
31284      */
31285     setVisible: function(visible){
31286         if(visible) {
31287             this.show();
31288         }else{
31289             this.hide();
31290         }
31291     },
31292     
31293     /**
31294      * Try to focus this item.
31295      */
31296     focus : function(){
31297         Roo.fly(this.el).focus();
31298     },
31299     
31300     /**
31301      * Disables this item.
31302      */
31303     disable : function(){
31304         Roo.fly(this.td).addClass("x-item-disabled");
31305         this.disabled = true;
31306         this.el.disabled = true;
31307     },
31308     
31309     /**
31310      * Enables this item.
31311      */
31312     enable : function(){
31313         Roo.fly(this.td).removeClass("x-item-disabled");
31314         this.disabled = false;
31315         this.el.disabled = false;
31316     }
31317 });
31318
31319
31320 /**
31321  * @class Roo.Toolbar.Separator
31322  * @extends Roo.Toolbar.Item
31323  * A simple toolbar separator class
31324  * @constructor
31325  * Creates a new Separator
31326  */
31327 Roo.Toolbar.Separator = function(cfg){
31328     
31329     var s = document.createElement("span");
31330     s.className = "ytb-sep";
31331     if (cfg) {
31332         cfg.el = s;
31333     }
31334     
31335     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31336 };
31337 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31338     enable:Roo.emptyFn,
31339     disable:Roo.emptyFn,
31340     focus:Roo.emptyFn
31341 });
31342
31343 /**
31344  * @class Roo.Toolbar.Spacer
31345  * @extends Roo.Toolbar.Item
31346  * A simple element that adds extra horizontal space to a toolbar.
31347  * @constructor
31348  * Creates a new Spacer
31349  */
31350 Roo.Toolbar.Spacer = function(cfg){
31351     var s = document.createElement("div");
31352     s.className = "ytb-spacer";
31353     if (cfg) {
31354         cfg.el = s;
31355     }
31356     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31357 };
31358 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31359     enable:Roo.emptyFn,
31360     disable:Roo.emptyFn,
31361     focus:Roo.emptyFn
31362 });
31363
31364 /**
31365  * @class Roo.Toolbar.Fill
31366  * @extends Roo.Toolbar.Spacer
31367  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31368  * @constructor
31369  * Creates a new Spacer
31370  */
31371 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31372     // private
31373     render : function(td){
31374         td.style.width = '100%';
31375         Roo.Toolbar.Fill.superclass.render.call(this, td);
31376     }
31377 });
31378
31379 /**
31380  * @class Roo.Toolbar.TextItem
31381  * @extends Roo.Toolbar.Item
31382  * A simple class that renders text directly into a toolbar.
31383  * @constructor
31384  * Creates a new TextItem
31385  * @cfg {string} text 
31386  */
31387 Roo.Toolbar.TextItem = function(cfg){
31388     var  text = cfg || "";
31389     if (typeof(cfg) == 'object') {
31390         text = cfg.text || "";
31391     }  else {
31392         cfg = null;
31393     }
31394     var s = document.createElement("span");
31395     s.className = "ytb-text";
31396     s.innerHTML = text;
31397     if (cfg) {
31398         cfg.el  = s;
31399     }
31400     
31401     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31402 };
31403 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31404     
31405      
31406     enable:Roo.emptyFn,
31407     disable:Roo.emptyFn,
31408     focus:Roo.emptyFn
31409 });
31410
31411 /**
31412  * @class Roo.Toolbar.Button
31413  * @extends Roo.Button
31414  * A button that renders into a toolbar.
31415  * @constructor
31416  * Creates a new Button
31417  * @param {Object} config A standard {@link Roo.Button} config object
31418  */
31419 Roo.Toolbar.Button = function(config){
31420     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31421 };
31422 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31423 {
31424     
31425     
31426     render : function(td){
31427         this.td = td;
31428         Roo.Toolbar.Button.superclass.render.call(this, td);
31429     },
31430     
31431     /**
31432      * Removes and destroys this button
31433      */
31434     destroy : function(){
31435         Roo.Toolbar.Button.superclass.destroy.call(this);
31436         this.td.parentNode.removeChild(this.td);
31437     },
31438     
31439     /**
31440      * Shows this button
31441      */
31442     show: function(){
31443         this.hidden = false;
31444         this.td.style.display = "";
31445     },
31446     
31447     /**
31448      * Hides this button
31449      */
31450     hide: function(){
31451         this.hidden = true;
31452         this.td.style.display = "none";
31453     },
31454
31455     /**
31456      * Disables this item
31457      */
31458     disable : function(){
31459         Roo.fly(this.td).addClass("x-item-disabled");
31460         this.disabled = true;
31461     },
31462
31463     /**
31464      * Enables this item
31465      */
31466     enable : function(){
31467         Roo.fly(this.td).removeClass("x-item-disabled");
31468         this.disabled = false;
31469     }
31470 });
31471 // backwards compat
31472 Roo.ToolbarButton = Roo.Toolbar.Button;
31473
31474 /**
31475  * @class Roo.Toolbar.SplitButton
31476  * @extends Roo.SplitButton
31477  * A menu button that renders into a toolbar.
31478  * @constructor
31479  * Creates a new SplitButton
31480  * @param {Object} config A standard {@link Roo.SplitButton} config object
31481  */
31482 Roo.Toolbar.SplitButton = function(config){
31483     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31484 };
31485 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31486     render : function(td){
31487         this.td = td;
31488         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31489     },
31490     
31491     /**
31492      * Removes and destroys this button
31493      */
31494     destroy : function(){
31495         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31496         this.td.parentNode.removeChild(this.td);
31497     },
31498     
31499     /**
31500      * Shows this button
31501      */
31502     show: function(){
31503         this.hidden = false;
31504         this.td.style.display = "";
31505     },
31506     
31507     /**
31508      * Hides this button
31509      */
31510     hide: function(){
31511         this.hidden = true;
31512         this.td.style.display = "none";
31513     }
31514 });
31515
31516 // backwards compat
31517 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31518  * Based on:
31519  * Ext JS Library 1.1.1
31520  * Copyright(c) 2006-2007, Ext JS, LLC.
31521  *
31522  * Originally Released Under LGPL - original licence link has changed is not relivant.
31523  *
31524  * Fork - LGPL
31525  * <script type="text/javascript">
31526  */
31527  
31528 /**
31529  * @class Roo.PagingToolbar
31530  * @extends Roo.Toolbar
31531  * @children   Roo.Toolbar.Item Roo.form.Field
31532  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31533  * @constructor
31534  * Create a new PagingToolbar
31535  * @param {Object} config The config object
31536  */
31537 Roo.PagingToolbar = function(el, ds, config)
31538 {
31539     // old args format still supported... - xtype is prefered..
31540     if (typeof(el) == 'object' && el.xtype) {
31541         // created from xtype...
31542         config = el;
31543         ds = el.dataSource;
31544         el = config.container;
31545     }
31546     var items = [];
31547     if (config.items) {
31548         items = config.items;
31549         config.items = [];
31550     }
31551     
31552     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31553     this.ds = ds;
31554     this.cursor = 0;
31555     this.renderButtons(this.el);
31556     this.bind(ds);
31557     
31558     // supprot items array.
31559    
31560     Roo.each(items, function(e) {
31561         this.add(Roo.factory(e));
31562     },this);
31563     
31564 };
31565
31566 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31567    
31568     /**
31569      * @cfg {String/HTMLElement/Element} container
31570      * container The id or element that will contain the toolbar
31571      */
31572     /**
31573      * @cfg {Boolean} displayInfo
31574      * True to display the displayMsg (defaults to false)
31575      */
31576     
31577     
31578     /**
31579      * @cfg {Number} pageSize
31580      * The number of records to display per page (defaults to 20)
31581      */
31582     pageSize: 20,
31583     /**
31584      * @cfg {String} displayMsg
31585      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31586      */
31587     displayMsg : 'Displaying {0} - {1} of {2}',
31588     /**
31589      * @cfg {String} emptyMsg
31590      * The message to display when no records are found (defaults to "No data to display")
31591      */
31592     emptyMsg : 'No data to display',
31593     /**
31594      * Customizable piece of the default paging text (defaults to "Page")
31595      * @type String
31596      */
31597     beforePageText : "Page",
31598     /**
31599      * Customizable piece of the default paging text (defaults to "of %0")
31600      * @type String
31601      */
31602     afterPageText : "of {0}",
31603     /**
31604      * Customizable piece of the default paging text (defaults to "First Page")
31605      * @type String
31606      */
31607     firstText : "First Page",
31608     /**
31609      * Customizable piece of the default paging text (defaults to "Previous Page")
31610      * @type String
31611      */
31612     prevText : "Previous Page",
31613     /**
31614      * Customizable piece of the default paging text (defaults to "Next Page")
31615      * @type String
31616      */
31617     nextText : "Next Page",
31618     /**
31619      * Customizable piece of the default paging text (defaults to "Last Page")
31620      * @type String
31621      */
31622     lastText : "Last Page",
31623     /**
31624      * Customizable piece of the default paging text (defaults to "Refresh")
31625      * @type String
31626      */
31627     refreshText : "Refresh",
31628
31629     // private
31630     renderButtons : function(el){
31631         Roo.PagingToolbar.superclass.render.call(this, el);
31632         this.first = this.addButton({
31633             tooltip: this.firstText,
31634             cls: "x-btn-icon x-grid-page-first",
31635             disabled: true,
31636             handler: this.onClick.createDelegate(this, ["first"])
31637         });
31638         this.prev = this.addButton({
31639             tooltip: this.prevText,
31640             cls: "x-btn-icon x-grid-page-prev",
31641             disabled: true,
31642             handler: this.onClick.createDelegate(this, ["prev"])
31643         });
31644         //this.addSeparator();
31645         this.add(this.beforePageText);
31646         this.field = Roo.get(this.addDom({
31647            tag: "input",
31648            type: "text",
31649            size: "3",
31650            value: "1",
31651            cls: "x-grid-page-number"
31652         }).el);
31653         this.field.on("keydown", this.onPagingKeydown, this);
31654         this.field.on("focus", function(){this.dom.select();});
31655         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31656         this.field.setHeight(18);
31657         //this.addSeparator();
31658         this.next = this.addButton({
31659             tooltip: this.nextText,
31660             cls: "x-btn-icon x-grid-page-next",
31661             disabled: true,
31662             handler: this.onClick.createDelegate(this, ["next"])
31663         });
31664         this.last = this.addButton({
31665             tooltip: this.lastText,
31666             cls: "x-btn-icon x-grid-page-last",
31667             disabled: true,
31668             handler: this.onClick.createDelegate(this, ["last"])
31669         });
31670         //this.addSeparator();
31671         this.loading = this.addButton({
31672             tooltip: this.refreshText,
31673             cls: "x-btn-icon x-grid-loading",
31674             handler: this.onClick.createDelegate(this, ["refresh"])
31675         });
31676
31677         if(this.displayInfo){
31678             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31679         }
31680     },
31681
31682     // private
31683     updateInfo : function(){
31684         if(this.displayEl){
31685             var count = this.ds.getCount();
31686             var msg = count == 0 ?
31687                 this.emptyMsg :
31688                 String.format(
31689                     this.displayMsg,
31690                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31691                 );
31692             this.displayEl.update(msg);
31693         }
31694     },
31695
31696     // private
31697     onLoad : function(ds, r, o){
31698        this.cursor = o.params ? o.params.start : 0;
31699        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31700
31701        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31702        this.field.dom.value = ap;
31703        this.first.setDisabled(ap == 1);
31704        this.prev.setDisabled(ap == 1);
31705        this.next.setDisabled(ap == ps);
31706        this.last.setDisabled(ap == ps);
31707        this.loading.enable();
31708        this.updateInfo();
31709     },
31710
31711     // private
31712     getPageData : function(){
31713         var total = this.ds.getTotalCount();
31714         return {
31715             total : total,
31716             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31717             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31718         };
31719     },
31720
31721     // private
31722     onLoadError : function(){
31723         this.loading.enable();
31724     },
31725
31726     // private
31727     onPagingKeydown : function(e){
31728         var k = e.getKey();
31729         var d = this.getPageData();
31730         if(k == e.RETURN){
31731             var v = this.field.dom.value, pageNum;
31732             if(!v || isNaN(pageNum = parseInt(v, 10))){
31733                 this.field.dom.value = d.activePage;
31734                 return;
31735             }
31736             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31737             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31738             e.stopEvent();
31739         }
31740         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))
31741         {
31742           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31743           this.field.dom.value = pageNum;
31744           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31745           e.stopEvent();
31746         }
31747         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31748         {
31749           var v = this.field.dom.value, pageNum; 
31750           var increment = (e.shiftKey) ? 10 : 1;
31751           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31752             increment *= -1;
31753           }
31754           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31755             this.field.dom.value = d.activePage;
31756             return;
31757           }
31758           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31759           {
31760             this.field.dom.value = parseInt(v, 10) + increment;
31761             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31762             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31763           }
31764           e.stopEvent();
31765         }
31766     },
31767
31768     // private
31769     beforeLoad : function(){
31770         if(this.loading){
31771             this.loading.disable();
31772         }
31773     },
31774
31775     // private
31776     onClick : function(which){
31777         var ds = this.ds;
31778         switch(which){
31779             case "first":
31780                 ds.load({params:{start: 0, limit: this.pageSize}});
31781             break;
31782             case "prev":
31783                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31784             break;
31785             case "next":
31786                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31787             break;
31788             case "last":
31789                 var total = ds.getTotalCount();
31790                 var extra = total % this.pageSize;
31791                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31792                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31793             break;
31794             case "refresh":
31795                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31796             break;
31797         }
31798     },
31799
31800     /**
31801      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31802      * @param {Roo.data.Store} store The data store to unbind
31803      */
31804     unbind : function(ds){
31805         ds.un("beforeload", this.beforeLoad, this);
31806         ds.un("load", this.onLoad, this);
31807         ds.un("loadexception", this.onLoadError, this);
31808         ds.un("remove", this.updateInfo, this);
31809         ds.un("add", this.updateInfo, this);
31810         this.ds = undefined;
31811     },
31812
31813     /**
31814      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31815      * @param {Roo.data.Store} store The data store to bind
31816      */
31817     bind : function(ds){
31818         ds.on("beforeload", this.beforeLoad, this);
31819         ds.on("load", this.onLoad, this);
31820         ds.on("loadexception", this.onLoadError, this);
31821         ds.on("remove", this.updateInfo, this);
31822         ds.on("add", this.updateInfo, this);
31823         this.ds = ds;
31824     }
31825 });/*
31826  * Based on:
31827  * Ext JS Library 1.1.1
31828  * Copyright(c) 2006-2007, Ext JS, LLC.
31829  *
31830  * Originally Released Under LGPL - original licence link has changed is not relivant.
31831  *
31832  * Fork - LGPL
31833  * <script type="text/javascript">
31834  */
31835
31836 /**
31837  * @class Roo.Resizable
31838  * @extends Roo.util.Observable
31839  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31840  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31841  * 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
31842  * the element will be wrapped for you automatically.</p>
31843  * <p>Here is the list of valid resize handles:</p>
31844  * <pre>
31845 Value   Description
31846 ------  -------------------
31847  'n'     north
31848  's'     south
31849  'e'     east
31850  'w'     west
31851  'nw'    northwest
31852  'sw'    southwest
31853  'se'    southeast
31854  'ne'    northeast
31855  'hd'    horizontal drag
31856  'all'   all
31857 </pre>
31858  * <p>Here's an example showing the creation of a typical Resizable:</p>
31859  * <pre><code>
31860 var resizer = new Roo.Resizable("element-id", {
31861     handles: 'all',
31862     minWidth: 200,
31863     minHeight: 100,
31864     maxWidth: 500,
31865     maxHeight: 400,
31866     pinned: true
31867 });
31868 resizer.on("resize", myHandler);
31869 </code></pre>
31870  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31871  * resizer.east.setDisplayed(false);</p>
31872  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31873  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31874  * resize operation's new size (defaults to [0, 0])
31875  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31876  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31877  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31878  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31879  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31880  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31881  * @cfg {Number} width The width of the element in pixels (defaults to null)
31882  * @cfg {Number} height The height of the element in pixels (defaults to null)
31883  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31884  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31885  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31886  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31887  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31888  * in favor of the handles config option (defaults to false)
31889  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31890  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31891  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31892  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31893  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31894  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31895  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31896  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31897  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31898  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31899  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31900  * @constructor
31901  * Create a new resizable component
31902  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31903  * @param {Object} config configuration options
31904   */
31905 Roo.Resizable = function(el, config)
31906 {
31907     this.el = Roo.get(el);
31908
31909     if(config && config.wrap){
31910         config.resizeChild = this.el;
31911         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31912         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31913         this.el.setStyle("overflow", "hidden");
31914         this.el.setPositioning(config.resizeChild.getPositioning());
31915         config.resizeChild.clearPositioning();
31916         if(!config.width || !config.height){
31917             var csize = config.resizeChild.getSize();
31918             this.el.setSize(csize.width, csize.height);
31919         }
31920         if(config.pinned && !config.adjustments){
31921             config.adjustments = "auto";
31922         }
31923     }
31924
31925     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31926     this.proxy.unselectable();
31927     this.proxy.enableDisplayMode('block');
31928
31929     Roo.apply(this, config);
31930
31931     if(this.pinned){
31932         this.disableTrackOver = true;
31933         this.el.addClass("x-resizable-pinned");
31934     }
31935     // if the element isn't positioned, make it relative
31936     var position = this.el.getStyle("position");
31937     if(position != "absolute" && position != "fixed"){
31938         this.el.setStyle("position", "relative");
31939     }
31940     if(!this.handles){ // no handles passed, must be legacy style
31941         this.handles = 's,e,se';
31942         if(this.multiDirectional){
31943             this.handles += ',n,w';
31944         }
31945     }
31946     if(this.handles == "all"){
31947         this.handles = "n s e w ne nw se sw";
31948     }
31949     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31950     var ps = Roo.Resizable.positions;
31951     for(var i = 0, len = hs.length; i < len; i++){
31952         if(hs[i] && ps[hs[i]]){
31953             var pos = ps[hs[i]];
31954             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31955         }
31956     }
31957     // legacy
31958     this.corner = this.southeast;
31959     
31960     // updateBox = the box can move..
31961     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31962         this.updateBox = true;
31963     }
31964
31965     this.activeHandle = null;
31966
31967     if(this.resizeChild){
31968         if(typeof this.resizeChild == "boolean"){
31969             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31970         }else{
31971             this.resizeChild = Roo.get(this.resizeChild, true);
31972         }
31973     }
31974     
31975     if(this.adjustments == "auto"){
31976         var rc = this.resizeChild;
31977         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31978         if(rc && (hw || hn)){
31979             rc.position("relative");
31980             rc.setLeft(hw ? hw.el.getWidth() : 0);
31981             rc.setTop(hn ? hn.el.getHeight() : 0);
31982         }
31983         this.adjustments = [
31984             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31985             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31986         ];
31987     }
31988
31989     if(this.draggable){
31990         this.dd = this.dynamic ?
31991             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31992         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31993     }
31994
31995     // public events
31996     this.addEvents({
31997         /**
31998          * @event beforeresize
31999          * Fired before resize is allowed. Set enabled to false to cancel resize.
32000          * @param {Roo.Resizable} this
32001          * @param {Roo.EventObject} e The mousedown event
32002          */
32003         "beforeresize" : true,
32004         /**
32005          * @event resizing
32006          * Fired a resizing.
32007          * @param {Roo.Resizable} this
32008          * @param {Number} x The new x position
32009          * @param {Number} y The new y position
32010          * @param {Number} w The new w width
32011          * @param {Number} h The new h hight
32012          * @param {Roo.EventObject} e The mouseup event
32013          */
32014         "resizing" : true,
32015         /**
32016          * @event resize
32017          * Fired after a resize.
32018          * @param {Roo.Resizable} this
32019          * @param {Number} width The new width
32020          * @param {Number} height The new height
32021          * @param {Roo.EventObject} e The mouseup event
32022          */
32023         "resize" : true
32024     });
32025
32026     if(this.width !== null && this.height !== null){
32027         this.resizeTo(this.width, this.height);
32028     }else{
32029         this.updateChildSize();
32030     }
32031     if(Roo.isIE){
32032         this.el.dom.style.zoom = 1;
32033     }
32034     Roo.Resizable.superclass.constructor.call(this);
32035 };
32036
32037 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32038         resizeChild : false,
32039         adjustments : [0, 0],
32040         minWidth : 5,
32041         minHeight : 5,
32042         maxWidth : 10000,
32043         maxHeight : 10000,
32044         enabled : true,
32045         animate : false,
32046         duration : .35,
32047         dynamic : false,
32048         handles : false,
32049         multiDirectional : false,
32050         disableTrackOver : false,
32051         easing : 'easeOutStrong',
32052         widthIncrement : 0,
32053         heightIncrement : 0,
32054         pinned : false,
32055         width : null,
32056         height : null,
32057         preserveRatio : false,
32058         transparent: false,
32059         minX: 0,
32060         minY: 0,
32061         draggable: false,
32062
32063         /**
32064          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32065          */
32066         constrainTo: undefined,
32067         /**
32068          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32069          */
32070         resizeRegion: undefined,
32071
32072
32073     /**
32074      * Perform a manual resize
32075      * @param {Number} width
32076      * @param {Number} height
32077      */
32078     resizeTo : function(width, height){
32079         this.el.setSize(width, height);
32080         this.updateChildSize();
32081         this.fireEvent("resize", this, width, height, null);
32082     },
32083
32084     // private
32085     startSizing : function(e, handle){
32086         this.fireEvent("beforeresize", this, e);
32087         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32088
32089             if(!this.overlay){
32090                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32091                 this.overlay.unselectable();
32092                 this.overlay.enableDisplayMode("block");
32093                 this.overlay.on("mousemove", this.onMouseMove, this);
32094                 this.overlay.on("mouseup", this.onMouseUp, this);
32095             }
32096             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32097
32098             this.resizing = true;
32099             this.startBox = this.el.getBox();
32100             this.startPoint = e.getXY();
32101             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32102                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32103
32104             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32105             this.overlay.show();
32106
32107             if(this.constrainTo) {
32108                 var ct = Roo.get(this.constrainTo);
32109                 this.resizeRegion = ct.getRegion().adjust(
32110                     ct.getFrameWidth('t'),
32111                     ct.getFrameWidth('l'),
32112                     -ct.getFrameWidth('b'),
32113                     -ct.getFrameWidth('r')
32114                 );
32115             }
32116
32117             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32118             this.proxy.show();
32119             this.proxy.setBox(this.startBox);
32120             if(!this.dynamic){
32121                 this.proxy.setStyle('visibility', 'visible');
32122             }
32123         }
32124     },
32125
32126     // private
32127     onMouseDown : function(handle, e){
32128         if(this.enabled){
32129             e.stopEvent();
32130             this.activeHandle = handle;
32131             this.startSizing(e, handle);
32132         }
32133     },
32134
32135     // private
32136     onMouseUp : function(e){
32137         var size = this.resizeElement();
32138         this.resizing = false;
32139         this.handleOut();
32140         this.overlay.hide();
32141         this.proxy.hide();
32142         this.fireEvent("resize", this, size.width, size.height, e);
32143     },
32144
32145     // private
32146     updateChildSize : function(){
32147         
32148         if(this.resizeChild){
32149             var el = this.el;
32150             var child = this.resizeChild;
32151             var adj = this.adjustments;
32152             if(el.dom.offsetWidth){
32153                 var b = el.getSize(true);
32154                 child.setSize(b.width+adj[0], b.height+adj[1]);
32155             }
32156             // Second call here for IE
32157             // The first call enables instant resizing and
32158             // the second call corrects scroll bars if they
32159             // exist
32160             if(Roo.isIE){
32161                 setTimeout(function(){
32162                     if(el.dom.offsetWidth){
32163                         var b = el.getSize(true);
32164                         child.setSize(b.width+adj[0], b.height+adj[1]);
32165                     }
32166                 }, 10);
32167             }
32168         }
32169     },
32170
32171     // private
32172     snap : function(value, inc, min){
32173         if(!inc || !value) {
32174             return value;
32175         }
32176         var newValue = value;
32177         var m = value % inc;
32178         if(m > 0){
32179             if(m > (inc/2)){
32180                 newValue = value + (inc-m);
32181             }else{
32182                 newValue = value - m;
32183             }
32184         }
32185         return Math.max(min, newValue);
32186     },
32187
32188     // private
32189     resizeElement : function(){
32190         var box = this.proxy.getBox();
32191         if(this.updateBox){
32192             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32193         }else{
32194             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32195         }
32196         this.updateChildSize();
32197         if(!this.dynamic){
32198             this.proxy.hide();
32199         }
32200         return box;
32201     },
32202
32203     // private
32204     constrain : function(v, diff, m, mx){
32205         if(v - diff < m){
32206             diff = v - m;
32207         }else if(v - diff > mx){
32208             diff = mx - v;
32209         }
32210         return diff;
32211     },
32212
32213     // private
32214     onMouseMove : function(e){
32215         
32216         if(this.enabled){
32217             try{// try catch so if something goes wrong the user doesn't get hung
32218
32219             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32220                 return;
32221             }
32222
32223             //var curXY = this.startPoint;
32224             var curSize = this.curSize || this.startBox;
32225             var x = this.startBox.x, y = this.startBox.y;
32226             var ox = x, oy = y;
32227             var w = curSize.width, h = curSize.height;
32228             var ow = w, oh = h;
32229             var mw = this.minWidth, mh = this.minHeight;
32230             var mxw = this.maxWidth, mxh = this.maxHeight;
32231             var wi = this.widthIncrement;
32232             var hi = this.heightIncrement;
32233
32234             var eventXY = e.getXY();
32235             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32236             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32237
32238             var pos = this.activeHandle.position;
32239
32240             switch(pos){
32241                 case "east":
32242                     w += diffX;
32243                     w = Math.min(Math.max(mw, w), mxw);
32244                     break;
32245              
32246                 case "south":
32247                     h += diffY;
32248                     h = Math.min(Math.max(mh, h), mxh);
32249                     break;
32250                 case "southeast":
32251                     w += diffX;
32252                     h += diffY;
32253                     w = Math.min(Math.max(mw, w), mxw);
32254                     h = Math.min(Math.max(mh, h), mxh);
32255                     break;
32256                 case "north":
32257                     diffY = this.constrain(h, diffY, mh, mxh);
32258                     y += diffY;
32259                     h -= diffY;
32260                     break;
32261                 case "hdrag":
32262                     
32263                     if (wi) {
32264                         var adiffX = Math.abs(diffX);
32265                         var sub = (adiffX % wi); // how much 
32266                         if (sub > (wi/2)) { // far enough to snap
32267                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32268                         } else {
32269                             // remove difference.. 
32270                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32271                         }
32272                     }
32273                     x += diffX;
32274                     x = Math.max(this.minX, x);
32275                     break;
32276                 case "west":
32277                     diffX = this.constrain(w, diffX, mw, mxw);
32278                     x += diffX;
32279                     w -= diffX;
32280                     break;
32281                 case "northeast":
32282                     w += diffX;
32283                     w = Math.min(Math.max(mw, w), mxw);
32284                     diffY = this.constrain(h, diffY, mh, mxh);
32285                     y += diffY;
32286                     h -= diffY;
32287                     break;
32288                 case "northwest":
32289                     diffX = this.constrain(w, diffX, mw, mxw);
32290                     diffY = this.constrain(h, diffY, mh, mxh);
32291                     y += diffY;
32292                     h -= diffY;
32293                     x += diffX;
32294                     w -= diffX;
32295                     break;
32296                case "southwest":
32297                     diffX = this.constrain(w, diffX, mw, mxw);
32298                     h += diffY;
32299                     h = Math.min(Math.max(mh, h), mxh);
32300                     x += diffX;
32301                     w -= diffX;
32302                     break;
32303             }
32304
32305             var sw = this.snap(w, wi, mw);
32306             var sh = this.snap(h, hi, mh);
32307             if(sw != w || sh != h){
32308                 switch(pos){
32309                     case "northeast":
32310                         y -= sh - h;
32311                     break;
32312                     case "north":
32313                         y -= sh - h;
32314                         break;
32315                     case "southwest":
32316                         x -= sw - w;
32317                     break;
32318                     case "west":
32319                         x -= sw - w;
32320                         break;
32321                     case "northwest":
32322                         x -= sw - w;
32323                         y -= sh - h;
32324                     break;
32325                 }
32326                 w = sw;
32327                 h = sh;
32328             }
32329
32330             if(this.preserveRatio){
32331                 switch(pos){
32332                     case "southeast":
32333                     case "east":
32334                         h = oh * (w/ow);
32335                         h = Math.min(Math.max(mh, h), mxh);
32336                         w = ow * (h/oh);
32337                        break;
32338                     case "south":
32339                         w = ow * (h/oh);
32340                         w = Math.min(Math.max(mw, w), mxw);
32341                         h = oh * (w/ow);
32342                         break;
32343                     case "northeast":
32344                         w = ow * (h/oh);
32345                         w = Math.min(Math.max(mw, w), mxw);
32346                         h = oh * (w/ow);
32347                     break;
32348                     case "north":
32349                         var tw = w;
32350                         w = ow * (h/oh);
32351                         w = Math.min(Math.max(mw, w), mxw);
32352                         h = oh * (w/ow);
32353                         x += (tw - w) / 2;
32354                         break;
32355                     case "southwest":
32356                         h = oh * (w/ow);
32357                         h = Math.min(Math.max(mh, h), mxh);
32358                         var tw = w;
32359                         w = ow * (h/oh);
32360                         x += tw - w;
32361                         break;
32362                     case "west":
32363                         var th = h;
32364                         h = oh * (w/ow);
32365                         h = Math.min(Math.max(mh, h), mxh);
32366                         y += (th - h) / 2;
32367                         var tw = w;
32368                         w = ow * (h/oh);
32369                         x += tw - w;
32370                        break;
32371                     case "northwest":
32372                         var tw = w;
32373                         var th = h;
32374                         h = oh * (w/ow);
32375                         h = Math.min(Math.max(mh, h), mxh);
32376                         w = ow * (h/oh);
32377                         y += th - h;
32378                         x += tw - w;
32379                        break;
32380
32381                 }
32382             }
32383             if (pos == 'hdrag') {
32384                 w = ow;
32385             }
32386             this.proxy.setBounds(x, y, w, h);
32387             if(this.dynamic){
32388                 this.resizeElement();
32389             }
32390             }catch(e){}
32391         }
32392         this.fireEvent("resizing", this, x, y, w, h, e);
32393     },
32394
32395     // private
32396     handleOver : function(){
32397         if(this.enabled){
32398             this.el.addClass("x-resizable-over");
32399         }
32400     },
32401
32402     // private
32403     handleOut : function(){
32404         if(!this.resizing){
32405             this.el.removeClass("x-resizable-over");
32406         }
32407     },
32408
32409     /**
32410      * Returns the element this component is bound to.
32411      * @return {Roo.Element}
32412      */
32413     getEl : function(){
32414         return this.el;
32415     },
32416
32417     /**
32418      * Returns the resizeChild element (or null).
32419      * @return {Roo.Element}
32420      */
32421     getResizeChild : function(){
32422         return this.resizeChild;
32423     },
32424     groupHandler : function()
32425     {
32426         
32427     },
32428     /**
32429      * Destroys this resizable. If the element was wrapped and
32430      * removeEl is not true then the element remains.
32431      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32432      */
32433     destroy : function(removeEl){
32434         this.proxy.remove();
32435         if(this.overlay){
32436             this.overlay.removeAllListeners();
32437             this.overlay.remove();
32438         }
32439         var ps = Roo.Resizable.positions;
32440         for(var k in ps){
32441             if(typeof ps[k] != "function" && this[ps[k]]){
32442                 var h = this[ps[k]];
32443                 h.el.removeAllListeners();
32444                 h.el.remove();
32445             }
32446         }
32447         if(removeEl){
32448             this.el.update("");
32449             this.el.remove();
32450         }
32451     }
32452 });
32453
32454 // private
32455 // hash to map config positions to true positions
32456 Roo.Resizable.positions = {
32457     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32458     hd: "hdrag"
32459 };
32460
32461 // private
32462 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32463     if(!this.tpl){
32464         // only initialize the template if resizable is used
32465         var tpl = Roo.DomHelper.createTemplate(
32466             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32467         );
32468         tpl.compile();
32469         Roo.Resizable.Handle.prototype.tpl = tpl;
32470     }
32471     this.position = pos;
32472     this.rz = rz;
32473     // show north drag fro topdra
32474     var handlepos = pos == 'hdrag' ? 'north' : pos;
32475     
32476     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32477     if (pos == 'hdrag') {
32478         this.el.setStyle('cursor', 'pointer');
32479     }
32480     this.el.unselectable();
32481     if(transparent){
32482         this.el.setOpacity(0);
32483     }
32484     this.el.on("mousedown", this.onMouseDown, this);
32485     if(!disableTrackOver){
32486         this.el.on("mouseover", this.onMouseOver, this);
32487         this.el.on("mouseout", this.onMouseOut, this);
32488     }
32489 };
32490
32491 // private
32492 Roo.Resizable.Handle.prototype = {
32493     afterResize : function(rz){
32494         Roo.log('after?');
32495         // do nothing
32496     },
32497     // private
32498     onMouseDown : function(e){
32499         this.rz.onMouseDown(this, e);
32500     },
32501     // private
32502     onMouseOver : function(e){
32503         this.rz.handleOver(this, e);
32504     },
32505     // private
32506     onMouseOut : function(e){
32507         this.rz.handleOut(this, e);
32508     }
32509 };/*
32510  * Based on:
32511  * Ext JS Library 1.1.1
32512  * Copyright(c) 2006-2007, Ext JS, LLC.
32513  *
32514  * Originally Released Under LGPL - original licence link has changed is not relivant.
32515  *
32516  * Fork - LGPL
32517  * <script type="text/javascript">
32518  */
32519
32520 /**
32521  * @class Roo.Editor
32522  * @extends Roo.Component
32523  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32524  * @constructor
32525  * Create a new Editor
32526  * @param {Roo.form.Field} field The Field object (or descendant)
32527  * @param {Object} config The config object
32528  */
32529 Roo.Editor = function(field, config){
32530     Roo.Editor.superclass.constructor.call(this, config);
32531     this.field = field;
32532     this.addEvents({
32533         /**
32534              * @event beforestartedit
32535              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32536              * false from the handler of this event.
32537              * @param {Editor} this
32538              * @param {Roo.Element} boundEl The underlying element bound to this editor
32539              * @param {Mixed} value The field value being set
32540              */
32541         "beforestartedit" : true,
32542         /**
32543              * @event startedit
32544              * Fires when this editor is displayed
32545              * @param {Roo.Element} boundEl The underlying element bound to this editor
32546              * @param {Mixed} value The starting field value
32547              */
32548         "startedit" : true,
32549         /**
32550              * @event beforecomplete
32551              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32552              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32553              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32554              * event will not fire since no edit actually occurred.
32555              * @param {Editor} this
32556              * @param {Mixed} value The current field value
32557              * @param {Mixed} startValue The original field value
32558              */
32559         "beforecomplete" : true,
32560         /**
32561              * @event complete
32562              * Fires after editing is complete and any changed value has been written to the underlying field.
32563              * @param {Editor} this
32564              * @param {Mixed} value The current field value
32565              * @param {Mixed} startValue The original field value
32566              */
32567         "complete" : true,
32568         /**
32569          * @event specialkey
32570          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32571          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32572          * @param {Roo.form.Field} this
32573          * @param {Roo.EventObject} e The event object
32574          */
32575         "specialkey" : true
32576     });
32577 };
32578
32579 Roo.extend(Roo.Editor, Roo.Component, {
32580     /**
32581      * @cfg {Boolean/String} autosize
32582      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32583      * or "height" to adopt the height only (defaults to false)
32584      */
32585     /**
32586      * @cfg {Boolean} revertInvalid
32587      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32588      * validation fails (defaults to true)
32589      */
32590     /**
32591      * @cfg {Boolean} ignoreNoChange
32592      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32593      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32594      * will never be ignored.
32595      */
32596     /**
32597      * @cfg {Boolean} hideEl
32598      * False to keep the bound element visible while the editor is displayed (defaults to true)
32599      */
32600     /**
32601      * @cfg {Mixed} value
32602      * The data value of the underlying field (defaults to "")
32603      */
32604     value : "",
32605     /**
32606      * @cfg {String} alignment
32607      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32608      */
32609     alignment: "c-c?",
32610     /**
32611      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32612      * for bottom-right shadow (defaults to "frame")
32613      */
32614     shadow : "frame",
32615     /**
32616      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32617      */
32618     constrain : false,
32619     /**
32620      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32621      */
32622     completeOnEnter : false,
32623     /**
32624      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32625      */
32626     cancelOnEsc : false,
32627     /**
32628      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32629      */
32630     updateEl : false,
32631
32632     // private
32633     onRender : function(ct, position){
32634         this.el = new Roo.Layer({
32635             shadow: this.shadow,
32636             cls: "x-editor",
32637             parentEl : ct,
32638             shim : this.shim,
32639             shadowOffset:4,
32640             id: this.id,
32641             constrain: this.constrain
32642         });
32643         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32644         if(this.field.msgTarget != 'title'){
32645             this.field.msgTarget = 'qtip';
32646         }
32647         this.field.render(this.el);
32648         if(Roo.isGecko){
32649             this.field.el.dom.setAttribute('autocomplete', 'off');
32650         }
32651         this.field.on("specialkey", this.onSpecialKey, this);
32652         if(this.swallowKeys){
32653             this.field.el.swallowEvent(['keydown','keypress']);
32654         }
32655         this.field.show();
32656         this.field.on("blur", this.onBlur, this);
32657         if(this.field.grow){
32658             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32659         }
32660     },
32661
32662     onSpecialKey : function(field, e)
32663     {
32664         //Roo.log('editor onSpecialKey');
32665         if(this.completeOnEnter && e.getKey() == e.ENTER){
32666             e.stopEvent();
32667             this.completeEdit();
32668             return;
32669         }
32670         // do not fire special key otherwise it might hide close the editor...
32671         if(e.getKey() == e.ENTER){    
32672             return;
32673         }
32674         if(this.cancelOnEsc && e.getKey() == e.ESC){
32675             this.cancelEdit();
32676             return;
32677         } 
32678         this.fireEvent('specialkey', field, e);
32679     
32680     },
32681
32682     /**
32683      * Starts the editing process and shows the editor.
32684      * @param {String/HTMLElement/Element} el The element to edit
32685      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32686       * to the innerHTML of el.
32687      */
32688     startEdit : function(el, value){
32689         if(this.editing){
32690             this.completeEdit();
32691         }
32692         this.boundEl = Roo.get(el);
32693         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32694         if(!this.rendered){
32695             this.render(this.parentEl || document.body);
32696         }
32697         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32698             return;
32699         }
32700         this.startValue = v;
32701         this.field.setValue(v);
32702         if(this.autoSize){
32703             var sz = this.boundEl.getSize();
32704             switch(this.autoSize){
32705                 case "width":
32706                 this.setSize(sz.width,  "");
32707                 break;
32708                 case "height":
32709                 this.setSize("",  sz.height);
32710                 break;
32711                 default:
32712                 this.setSize(sz.width,  sz.height);
32713             }
32714         }
32715         this.el.alignTo(this.boundEl, this.alignment);
32716         this.editing = true;
32717         if(Roo.QuickTips){
32718             Roo.QuickTips.disable();
32719         }
32720         this.show();
32721     },
32722
32723     /**
32724      * Sets the height and width of this editor.
32725      * @param {Number} width The new width
32726      * @param {Number} height The new height
32727      */
32728     setSize : function(w, h){
32729         this.field.setSize(w, h);
32730         if(this.el){
32731             this.el.sync();
32732         }
32733     },
32734
32735     /**
32736      * Realigns the editor to the bound field based on the current alignment config value.
32737      */
32738     realign : function(){
32739         this.el.alignTo(this.boundEl, this.alignment);
32740     },
32741
32742     /**
32743      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32744      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32745      */
32746     completeEdit : function(remainVisible){
32747         if(!this.editing){
32748             return;
32749         }
32750         var v = this.getValue();
32751         if(this.revertInvalid !== false && !this.field.isValid()){
32752             v = this.startValue;
32753             this.cancelEdit(true);
32754         }
32755         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32756             this.editing = false;
32757             this.hide();
32758             return;
32759         }
32760         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32761             this.editing = false;
32762             if(this.updateEl && this.boundEl){
32763                 this.boundEl.update(v);
32764             }
32765             if(remainVisible !== true){
32766                 this.hide();
32767             }
32768             this.fireEvent("complete", this, v, this.startValue);
32769         }
32770     },
32771
32772     // private
32773     onShow : function(){
32774         this.el.show();
32775         if(this.hideEl !== false){
32776             this.boundEl.hide();
32777         }
32778         this.field.show();
32779         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32780             this.fixIEFocus = true;
32781             this.deferredFocus.defer(50, this);
32782         }else{
32783             this.field.focus();
32784         }
32785         this.fireEvent("startedit", this.boundEl, this.startValue);
32786     },
32787
32788     deferredFocus : function(){
32789         if(this.editing){
32790             this.field.focus();
32791         }
32792     },
32793
32794     /**
32795      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32796      * reverted to the original starting value.
32797      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32798      * cancel (defaults to false)
32799      */
32800     cancelEdit : function(remainVisible){
32801         if(this.editing){
32802             this.setValue(this.startValue);
32803             if(remainVisible !== true){
32804                 this.hide();
32805             }
32806         }
32807     },
32808
32809     // private
32810     onBlur : function(){
32811         if(this.allowBlur !== true && this.editing){
32812             this.completeEdit();
32813         }
32814     },
32815
32816     // private
32817     onHide : function(){
32818         if(this.editing){
32819             this.completeEdit();
32820             return;
32821         }
32822         this.field.blur();
32823         if(this.field.collapse){
32824             this.field.collapse();
32825         }
32826         this.el.hide();
32827         if(this.hideEl !== false){
32828             this.boundEl.show();
32829         }
32830         if(Roo.QuickTips){
32831             Roo.QuickTips.enable();
32832         }
32833     },
32834
32835     /**
32836      * Sets the data value of the editor
32837      * @param {Mixed} value Any valid value supported by the underlying field
32838      */
32839     setValue : function(v){
32840         this.field.setValue(v);
32841     },
32842
32843     /**
32844      * Gets the data value of the editor
32845      * @return {Mixed} The data value
32846      */
32847     getValue : function(){
32848         return this.field.getValue();
32849     }
32850 });/*
32851  * Based on:
32852  * Ext JS Library 1.1.1
32853  * Copyright(c) 2006-2007, Ext JS, LLC.
32854  *
32855  * Originally Released Under LGPL - original licence link has changed is not relivant.
32856  *
32857  * Fork - LGPL
32858  * <script type="text/javascript">
32859  */
32860  
32861 /**
32862  * @class Roo.BasicDialog
32863  * @extends Roo.util.Observable
32864  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32865  * <pre><code>
32866 var dlg = new Roo.BasicDialog("my-dlg", {
32867     height: 200,
32868     width: 300,
32869     minHeight: 100,
32870     minWidth: 150,
32871     modal: true,
32872     proxyDrag: true,
32873     shadow: true
32874 });
32875 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32876 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32877 dlg.addButton('Cancel', dlg.hide, dlg);
32878 dlg.show();
32879 </code></pre>
32880   <b>A Dialog should always be a direct child of the body element.</b>
32881  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32882  * @cfg {String} title Default text to display in the title bar (defaults to null)
32883  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32884  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32885  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32886  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32887  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32888  * (defaults to null with no animation)
32889  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32890  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32891  * property for valid values (defaults to 'all')
32892  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32893  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32894  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32895  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32896  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32897  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32898  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32899  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32900  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32901  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32902  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32903  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32904  * draggable = true (defaults to false)
32905  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32906  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32907  * shadow (defaults to false)
32908  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32909  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32910  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32911  * @cfg {Array} buttons Array of buttons
32912  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32913  * @constructor
32914  * Create a new BasicDialog.
32915  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32916  * @param {Object} config Configuration options
32917  */
32918 Roo.BasicDialog = function(el, config){
32919     this.el = Roo.get(el);
32920     var dh = Roo.DomHelper;
32921     if(!this.el && config && config.autoCreate){
32922         if(typeof config.autoCreate == "object"){
32923             if(!config.autoCreate.id){
32924                 config.autoCreate.id = el;
32925             }
32926             this.el = dh.append(document.body,
32927                         config.autoCreate, true);
32928         }else{
32929             this.el = dh.append(document.body,
32930                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32931         }
32932     }
32933     el = this.el;
32934     el.setDisplayed(true);
32935     el.hide = this.hideAction;
32936     this.id = el.id;
32937     el.addClass("x-dlg");
32938
32939     Roo.apply(this, config);
32940
32941     this.proxy = el.createProxy("x-dlg-proxy");
32942     this.proxy.hide = this.hideAction;
32943     this.proxy.setOpacity(.5);
32944     this.proxy.hide();
32945
32946     if(config.width){
32947         el.setWidth(config.width);
32948     }
32949     if(config.height){
32950         el.setHeight(config.height);
32951     }
32952     this.size = el.getSize();
32953     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32954         this.xy = [config.x,config.y];
32955     }else{
32956         this.xy = el.getCenterXY(true);
32957     }
32958     /** The header element @type Roo.Element */
32959     this.header = el.child("> .x-dlg-hd");
32960     /** The body element @type Roo.Element */
32961     this.body = el.child("> .x-dlg-bd");
32962     /** The footer element @type Roo.Element */
32963     this.footer = el.child("> .x-dlg-ft");
32964
32965     if(!this.header){
32966         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32967     }
32968     if(!this.body){
32969         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32970     }
32971
32972     this.header.unselectable();
32973     if(this.title){
32974         this.header.update(this.title);
32975     }
32976     // this element allows the dialog to be focused for keyboard event
32977     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32978     this.focusEl.swallowEvent("click", true);
32979
32980     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32981
32982     // wrap the body and footer for special rendering
32983     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32984     if(this.footer){
32985         this.bwrap.dom.appendChild(this.footer.dom);
32986     }
32987
32988     this.bg = this.el.createChild({
32989         tag: "div", cls:"x-dlg-bg",
32990         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32991     });
32992     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32993
32994
32995     if(this.autoScroll !== false && !this.autoTabs){
32996         this.body.setStyle("overflow", "auto");
32997     }
32998
32999     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33000
33001     if(this.closable !== false){
33002         this.el.addClass("x-dlg-closable");
33003         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33004         this.close.on("click", this.closeClick, this);
33005         this.close.addClassOnOver("x-dlg-close-over");
33006     }
33007     if(this.collapsible !== false){
33008         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33009         this.collapseBtn.on("click", this.collapseClick, this);
33010         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33011         this.header.on("dblclick", this.collapseClick, this);
33012     }
33013     if(this.resizable !== false){
33014         this.el.addClass("x-dlg-resizable");
33015         this.resizer = new Roo.Resizable(el, {
33016             minWidth: this.minWidth || 80,
33017             minHeight:this.minHeight || 80,
33018             handles: this.resizeHandles || "all",
33019             pinned: true
33020         });
33021         this.resizer.on("beforeresize", this.beforeResize, this);
33022         this.resizer.on("resize", this.onResize, this);
33023     }
33024     if(this.draggable !== false){
33025         el.addClass("x-dlg-draggable");
33026         if (!this.proxyDrag) {
33027             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33028         }
33029         else {
33030             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33031         }
33032         dd.setHandleElId(this.header.id);
33033         dd.endDrag = this.endMove.createDelegate(this);
33034         dd.startDrag = this.startMove.createDelegate(this);
33035         dd.onDrag = this.onDrag.createDelegate(this);
33036         dd.scroll = false;
33037         this.dd = dd;
33038     }
33039     if(this.modal){
33040         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33041         this.mask.enableDisplayMode("block");
33042         this.mask.hide();
33043         this.el.addClass("x-dlg-modal");
33044     }
33045     if(this.shadow){
33046         this.shadow = new Roo.Shadow({
33047             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33048             offset : this.shadowOffset
33049         });
33050     }else{
33051         this.shadowOffset = 0;
33052     }
33053     if(Roo.useShims && this.shim !== false){
33054         this.shim = this.el.createShim();
33055         this.shim.hide = this.hideAction;
33056         this.shim.hide();
33057     }else{
33058         this.shim = false;
33059     }
33060     if(this.autoTabs){
33061         this.initTabs();
33062     }
33063     if (this.buttons) { 
33064         var bts= this.buttons;
33065         this.buttons = [];
33066         Roo.each(bts, function(b) {
33067             this.addButton(b);
33068         }, this);
33069     }
33070     
33071     
33072     this.addEvents({
33073         /**
33074          * @event keydown
33075          * Fires when a key is pressed
33076          * @param {Roo.BasicDialog} this
33077          * @param {Roo.EventObject} e
33078          */
33079         "keydown" : true,
33080         /**
33081          * @event move
33082          * Fires when this dialog is moved by the user.
33083          * @param {Roo.BasicDialog} this
33084          * @param {Number} x The new page X
33085          * @param {Number} y The new page Y
33086          */
33087         "move" : true,
33088         /**
33089          * @event resize
33090          * Fires when this dialog is resized by the user.
33091          * @param {Roo.BasicDialog} this
33092          * @param {Number} width The new width
33093          * @param {Number} height The new height
33094          */
33095         "resize" : true,
33096         /**
33097          * @event beforehide
33098          * Fires before this dialog is hidden.
33099          * @param {Roo.BasicDialog} this
33100          */
33101         "beforehide" : true,
33102         /**
33103          * @event hide
33104          * Fires when this dialog is hidden.
33105          * @param {Roo.BasicDialog} this
33106          */
33107         "hide" : true,
33108         /**
33109          * @event beforeshow
33110          * Fires before this dialog is shown.
33111          * @param {Roo.BasicDialog} this
33112          */
33113         "beforeshow" : true,
33114         /**
33115          * @event show
33116          * Fires when this dialog is shown.
33117          * @param {Roo.BasicDialog} this
33118          */
33119         "show" : true
33120     });
33121     el.on("keydown", this.onKeyDown, this);
33122     el.on("mousedown", this.toFront, this);
33123     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33124     this.el.hide();
33125     Roo.DialogManager.register(this);
33126     Roo.BasicDialog.superclass.constructor.call(this);
33127 };
33128
33129 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33130     shadowOffset: Roo.isIE ? 6 : 5,
33131     minHeight: 80,
33132     minWidth: 200,
33133     minButtonWidth: 75,
33134     defaultButton: null,
33135     buttonAlign: "right",
33136     tabTag: 'div',
33137     firstShow: true,
33138
33139     /**
33140      * Sets the dialog title text
33141      * @param {String} text The title text to display
33142      * @return {Roo.BasicDialog} this
33143      */
33144     setTitle : function(text){
33145         this.header.update(text);
33146         return this;
33147     },
33148
33149     // private
33150     closeClick : function(){
33151         this.hide();
33152     },
33153
33154     // private
33155     collapseClick : function(){
33156         this[this.collapsed ? "expand" : "collapse"]();
33157     },
33158
33159     /**
33160      * Collapses the dialog to its minimized state (only the title bar is visible).
33161      * Equivalent to the user clicking the collapse dialog button.
33162      */
33163     collapse : function(){
33164         if(!this.collapsed){
33165             this.collapsed = true;
33166             this.el.addClass("x-dlg-collapsed");
33167             this.restoreHeight = this.el.getHeight();
33168             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33169         }
33170     },
33171
33172     /**
33173      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33174      * clicking the expand dialog button.
33175      */
33176     expand : function(){
33177         if(this.collapsed){
33178             this.collapsed = false;
33179             this.el.removeClass("x-dlg-collapsed");
33180             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33181         }
33182     },
33183
33184     /**
33185      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33186      * @return {Roo.TabPanel} The tabs component
33187      */
33188     initTabs : function(){
33189         var tabs = this.getTabs();
33190         while(tabs.getTab(0)){
33191             tabs.removeTab(0);
33192         }
33193         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33194             var dom = el.dom;
33195             tabs.addTab(Roo.id(dom), dom.title);
33196             dom.title = "";
33197         });
33198         tabs.activate(0);
33199         return tabs;
33200     },
33201
33202     // private
33203     beforeResize : function(){
33204         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33205     },
33206
33207     // private
33208     onResize : function(){
33209         this.refreshSize();
33210         this.syncBodyHeight();
33211         this.adjustAssets();
33212         this.focus();
33213         this.fireEvent("resize", this, this.size.width, this.size.height);
33214     },
33215
33216     // private
33217     onKeyDown : function(e){
33218         if(this.isVisible()){
33219             this.fireEvent("keydown", this, e);
33220         }
33221     },
33222
33223     /**
33224      * Resizes the dialog.
33225      * @param {Number} width
33226      * @param {Number} height
33227      * @return {Roo.BasicDialog} this
33228      */
33229     resizeTo : function(width, height){
33230         this.el.setSize(width, height);
33231         this.size = {width: width, height: height};
33232         this.syncBodyHeight();
33233         if(this.fixedcenter){
33234             this.center();
33235         }
33236         if(this.isVisible()){
33237             this.constrainXY();
33238             this.adjustAssets();
33239         }
33240         this.fireEvent("resize", this, width, height);
33241         return this;
33242     },
33243
33244
33245     /**
33246      * Resizes the dialog to fit the specified content size.
33247      * @param {Number} width
33248      * @param {Number} height
33249      * @return {Roo.BasicDialog} this
33250      */
33251     setContentSize : function(w, h){
33252         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33253         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33254         //if(!this.el.isBorderBox()){
33255             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33256             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33257         //}
33258         if(this.tabs){
33259             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33260             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33261         }
33262         this.resizeTo(w, h);
33263         return this;
33264     },
33265
33266     /**
33267      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33268      * executed in response to a particular key being pressed while the dialog is active.
33269      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33270      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33271      * @param {Function} fn The function to call
33272      * @param {Object} scope (optional) The scope of the function
33273      * @return {Roo.BasicDialog} this
33274      */
33275     addKeyListener : function(key, fn, scope){
33276         var keyCode, shift, ctrl, alt;
33277         if(typeof key == "object" && !(key instanceof Array)){
33278             keyCode = key["key"];
33279             shift = key["shift"];
33280             ctrl = key["ctrl"];
33281             alt = key["alt"];
33282         }else{
33283             keyCode = key;
33284         }
33285         var handler = function(dlg, e){
33286             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33287                 var k = e.getKey();
33288                 if(keyCode instanceof Array){
33289                     for(var i = 0, len = keyCode.length; i < len; i++){
33290                         if(keyCode[i] == k){
33291                           fn.call(scope || window, dlg, k, e);
33292                           return;
33293                         }
33294                     }
33295                 }else{
33296                     if(k == keyCode){
33297                         fn.call(scope || window, dlg, k, e);
33298                     }
33299                 }
33300             }
33301         };
33302         this.on("keydown", handler);
33303         return this;
33304     },
33305
33306     /**
33307      * Returns the TabPanel component (creates it if it doesn't exist).
33308      * Note: If you wish to simply check for the existence of tabs without creating them,
33309      * check for a null 'tabs' property.
33310      * @return {Roo.TabPanel} The tabs component
33311      */
33312     getTabs : function(){
33313         if(!this.tabs){
33314             this.el.addClass("x-dlg-auto-tabs");
33315             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33316             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33317         }
33318         return this.tabs;
33319     },
33320
33321     /**
33322      * Adds a button to the footer section of the dialog.
33323      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33324      * object or a valid Roo.DomHelper element config
33325      * @param {Function} handler The function called when the button is clicked
33326      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33327      * @return {Roo.Button} The new button
33328      */
33329     addButton : function(config, handler, scope){
33330         var dh = Roo.DomHelper;
33331         if(!this.footer){
33332             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33333         }
33334         if(!this.btnContainer){
33335             var tb = this.footer.createChild({
33336
33337                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33338                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33339             }, null, true);
33340             this.btnContainer = tb.firstChild.firstChild.firstChild;
33341         }
33342         var bconfig = {
33343             handler: handler,
33344             scope: scope,
33345             minWidth: this.minButtonWidth,
33346             hideParent:true
33347         };
33348         if(typeof config == "string"){
33349             bconfig.text = config;
33350         }else{
33351             if(config.tag){
33352                 bconfig.dhconfig = config;
33353             }else{
33354                 Roo.apply(bconfig, config);
33355             }
33356         }
33357         var fc = false;
33358         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33359             bconfig.position = Math.max(0, bconfig.position);
33360             fc = this.btnContainer.childNodes[bconfig.position];
33361         }
33362          
33363         var btn = new Roo.Button(
33364             fc ? 
33365                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33366                 : this.btnContainer.appendChild(document.createElement("td")),
33367             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33368             bconfig
33369         );
33370         this.syncBodyHeight();
33371         if(!this.buttons){
33372             /**
33373              * Array of all the buttons that have been added to this dialog via addButton
33374              * @type Array
33375              */
33376             this.buttons = [];
33377         }
33378         this.buttons.push(btn);
33379         return btn;
33380     },
33381
33382     /**
33383      * Sets the default button to be focused when the dialog is displayed.
33384      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33385      * @return {Roo.BasicDialog} this
33386      */
33387     setDefaultButton : function(btn){
33388         this.defaultButton = btn;
33389         return this;
33390     },
33391
33392     // private
33393     getHeaderFooterHeight : function(safe){
33394         var height = 0;
33395         if(this.header){
33396            height += this.header.getHeight();
33397         }
33398         if(this.footer){
33399            var fm = this.footer.getMargins();
33400             height += (this.footer.getHeight()+fm.top+fm.bottom);
33401         }
33402         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33403         height += this.centerBg.getPadding("tb");
33404         return height;
33405     },
33406
33407     // private
33408     syncBodyHeight : function()
33409     {
33410         var bd = this.body, // the text
33411             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33412             bw = this.bwrap;
33413         var height = this.size.height - this.getHeaderFooterHeight(false);
33414         bd.setHeight(height-bd.getMargins("tb"));
33415         var hh = this.header.getHeight();
33416         var h = this.size.height-hh;
33417         cb.setHeight(h);
33418         
33419         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33420         bw.setHeight(h-cb.getPadding("tb"));
33421         
33422         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33423         bd.setWidth(bw.getWidth(true));
33424         if(this.tabs){
33425             this.tabs.syncHeight();
33426             if(Roo.isIE){
33427                 this.tabs.el.repaint();
33428             }
33429         }
33430     },
33431
33432     /**
33433      * Restores the previous state of the dialog if Roo.state is configured.
33434      * @return {Roo.BasicDialog} this
33435      */
33436     restoreState : function(){
33437         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33438         if(box && box.width){
33439             this.xy = [box.x, box.y];
33440             this.resizeTo(box.width, box.height);
33441         }
33442         return this;
33443     },
33444
33445     // private
33446     beforeShow : function(){
33447         this.expand();
33448         if(this.fixedcenter){
33449             this.xy = this.el.getCenterXY(true);
33450         }
33451         if(this.modal){
33452             Roo.get(document.body).addClass("x-body-masked");
33453             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33454             this.mask.show();
33455         }
33456         this.constrainXY();
33457     },
33458
33459     // private
33460     animShow : function(){
33461         var b = Roo.get(this.animateTarget).getBox();
33462         this.proxy.setSize(b.width, b.height);
33463         this.proxy.setLocation(b.x, b.y);
33464         this.proxy.show();
33465         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33466                     true, .35, this.showEl.createDelegate(this));
33467     },
33468
33469     /**
33470      * Shows the dialog.
33471      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33472      * @return {Roo.BasicDialog} this
33473      */
33474     show : function(animateTarget){
33475         if (this.fireEvent("beforeshow", this) === false){
33476             return;
33477         }
33478         if(this.syncHeightBeforeShow){
33479             this.syncBodyHeight();
33480         }else if(this.firstShow){
33481             this.firstShow = false;
33482             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33483         }
33484         this.animateTarget = animateTarget || this.animateTarget;
33485         if(!this.el.isVisible()){
33486             this.beforeShow();
33487             if(this.animateTarget && Roo.get(this.animateTarget)){
33488                 this.animShow();
33489             }else{
33490                 this.showEl();
33491             }
33492         }
33493         return this;
33494     },
33495
33496     // private
33497     showEl : function(){
33498         this.proxy.hide();
33499         this.el.setXY(this.xy);
33500         this.el.show();
33501         this.adjustAssets(true);
33502         this.toFront();
33503         this.focus();
33504         // IE peekaboo bug - fix found by Dave Fenwick
33505         if(Roo.isIE){
33506             this.el.repaint();
33507         }
33508         this.fireEvent("show", this);
33509     },
33510
33511     /**
33512      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33513      * dialog itself will receive focus.
33514      */
33515     focus : function(){
33516         if(this.defaultButton){
33517             this.defaultButton.focus();
33518         }else{
33519             this.focusEl.focus();
33520         }
33521     },
33522
33523     // private
33524     constrainXY : function(){
33525         if(this.constraintoviewport !== false){
33526             if(!this.viewSize){
33527                 if(this.container){
33528                     var s = this.container.getSize();
33529                     this.viewSize = [s.width, s.height];
33530                 }else{
33531                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33532                 }
33533             }
33534             var s = Roo.get(this.container||document).getScroll();
33535
33536             var x = this.xy[0], y = this.xy[1];
33537             var w = this.size.width, h = this.size.height;
33538             var vw = this.viewSize[0], vh = this.viewSize[1];
33539             // only move it if it needs it
33540             var moved = false;
33541             // first validate right/bottom
33542             if(x + w > vw+s.left){
33543                 x = vw - w;
33544                 moved = true;
33545             }
33546             if(y + h > vh+s.top){
33547                 y = vh - h;
33548                 moved = true;
33549             }
33550             // then make sure top/left isn't negative
33551             if(x < s.left){
33552                 x = s.left;
33553                 moved = true;
33554             }
33555             if(y < s.top){
33556                 y = s.top;
33557                 moved = true;
33558             }
33559             if(moved){
33560                 // cache xy
33561                 this.xy = [x, y];
33562                 if(this.isVisible()){
33563                     this.el.setLocation(x, y);
33564                     this.adjustAssets();
33565                 }
33566             }
33567         }
33568     },
33569
33570     // private
33571     onDrag : function(){
33572         if(!this.proxyDrag){
33573             this.xy = this.el.getXY();
33574             this.adjustAssets();
33575         }
33576     },
33577
33578     // private
33579     adjustAssets : function(doShow){
33580         var x = this.xy[0], y = this.xy[1];
33581         var w = this.size.width, h = this.size.height;
33582         if(doShow === true){
33583             if(this.shadow){
33584                 this.shadow.show(this.el);
33585             }
33586             if(this.shim){
33587                 this.shim.show();
33588             }
33589         }
33590         if(this.shadow && this.shadow.isVisible()){
33591             this.shadow.show(this.el);
33592         }
33593         if(this.shim && this.shim.isVisible()){
33594             this.shim.setBounds(x, y, w, h);
33595         }
33596     },
33597
33598     // private
33599     adjustViewport : function(w, h){
33600         if(!w || !h){
33601             w = Roo.lib.Dom.getViewWidth();
33602             h = Roo.lib.Dom.getViewHeight();
33603         }
33604         // cache the size
33605         this.viewSize = [w, h];
33606         if(this.modal && this.mask.isVisible()){
33607             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33608             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33609         }
33610         if(this.isVisible()){
33611             this.constrainXY();
33612         }
33613     },
33614
33615     /**
33616      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33617      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33618      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33619      */
33620     destroy : function(removeEl){
33621         if(this.isVisible()){
33622             this.animateTarget = null;
33623             this.hide();
33624         }
33625         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33626         if(this.tabs){
33627             this.tabs.destroy(removeEl);
33628         }
33629         Roo.destroy(
33630              this.shim,
33631              this.proxy,
33632              this.resizer,
33633              this.close,
33634              this.mask
33635         );
33636         if(this.dd){
33637             this.dd.unreg();
33638         }
33639         if(this.buttons){
33640            for(var i = 0, len = this.buttons.length; i < len; i++){
33641                this.buttons[i].destroy();
33642            }
33643         }
33644         this.el.removeAllListeners();
33645         if(removeEl === true){
33646             this.el.update("");
33647             this.el.remove();
33648         }
33649         Roo.DialogManager.unregister(this);
33650     },
33651
33652     // private
33653     startMove : function(){
33654         if(this.proxyDrag){
33655             this.proxy.show();
33656         }
33657         if(this.constraintoviewport !== false){
33658             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33659         }
33660     },
33661
33662     // private
33663     endMove : function(){
33664         if(!this.proxyDrag){
33665             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33666         }else{
33667             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33668             this.proxy.hide();
33669         }
33670         this.refreshSize();
33671         this.adjustAssets();
33672         this.focus();
33673         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33674     },
33675
33676     /**
33677      * Brings this dialog to the front of any other visible dialogs
33678      * @return {Roo.BasicDialog} this
33679      */
33680     toFront : function(){
33681         Roo.DialogManager.bringToFront(this);
33682         return this;
33683     },
33684
33685     /**
33686      * Sends this dialog to the back (under) of any other visible dialogs
33687      * @return {Roo.BasicDialog} this
33688      */
33689     toBack : function(){
33690         Roo.DialogManager.sendToBack(this);
33691         return this;
33692     },
33693
33694     /**
33695      * Centers this dialog in the viewport
33696      * @return {Roo.BasicDialog} this
33697      */
33698     center : function(){
33699         var xy = this.el.getCenterXY(true);
33700         this.moveTo(xy[0], xy[1]);
33701         return this;
33702     },
33703
33704     /**
33705      * Moves the dialog's top-left corner to the specified point
33706      * @param {Number} x
33707      * @param {Number} y
33708      * @return {Roo.BasicDialog} this
33709      */
33710     moveTo : function(x, y){
33711         this.xy = [x,y];
33712         if(this.isVisible()){
33713             this.el.setXY(this.xy);
33714             this.adjustAssets();
33715         }
33716         return this;
33717     },
33718
33719     /**
33720      * Aligns the dialog to the specified element
33721      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33722      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33723      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33724      * @return {Roo.BasicDialog} this
33725      */
33726     alignTo : function(element, position, offsets){
33727         this.xy = this.el.getAlignToXY(element, position, offsets);
33728         if(this.isVisible()){
33729             this.el.setXY(this.xy);
33730             this.adjustAssets();
33731         }
33732         return this;
33733     },
33734
33735     /**
33736      * Anchors an element to another element and realigns it when the window is resized.
33737      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33738      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33739      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33740      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33741      * is a number, it is used as the buffer delay (defaults to 50ms).
33742      * @return {Roo.BasicDialog} this
33743      */
33744     anchorTo : function(el, alignment, offsets, monitorScroll){
33745         var action = function(){
33746             this.alignTo(el, alignment, offsets);
33747         };
33748         Roo.EventManager.onWindowResize(action, this);
33749         var tm = typeof monitorScroll;
33750         if(tm != 'undefined'){
33751             Roo.EventManager.on(window, 'scroll', action, this,
33752                 {buffer: tm == 'number' ? monitorScroll : 50});
33753         }
33754         action.call(this);
33755         return this;
33756     },
33757
33758     /**
33759      * Returns true if the dialog is visible
33760      * @return {Boolean}
33761      */
33762     isVisible : function(){
33763         return this.el.isVisible();
33764     },
33765
33766     // private
33767     animHide : function(callback){
33768         var b = Roo.get(this.animateTarget).getBox();
33769         this.proxy.show();
33770         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33771         this.el.hide();
33772         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33773                     this.hideEl.createDelegate(this, [callback]));
33774     },
33775
33776     /**
33777      * Hides the dialog.
33778      * @param {Function} callback (optional) Function to call when the dialog is hidden
33779      * @return {Roo.BasicDialog} this
33780      */
33781     hide : function(callback){
33782         if (this.fireEvent("beforehide", this) === false){
33783             return;
33784         }
33785         if(this.shadow){
33786             this.shadow.hide();
33787         }
33788         if(this.shim) {
33789           this.shim.hide();
33790         }
33791         // sometimes animateTarget seems to get set.. causing problems...
33792         // this just double checks..
33793         if(this.animateTarget && Roo.get(this.animateTarget)) {
33794            this.animHide(callback);
33795         }else{
33796             this.el.hide();
33797             this.hideEl(callback);
33798         }
33799         return this;
33800     },
33801
33802     // private
33803     hideEl : function(callback){
33804         this.proxy.hide();
33805         if(this.modal){
33806             this.mask.hide();
33807             Roo.get(document.body).removeClass("x-body-masked");
33808         }
33809         this.fireEvent("hide", this);
33810         if(typeof callback == "function"){
33811             callback();
33812         }
33813     },
33814
33815     // private
33816     hideAction : function(){
33817         this.setLeft("-10000px");
33818         this.setTop("-10000px");
33819         this.setStyle("visibility", "hidden");
33820     },
33821
33822     // private
33823     refreshSize : function(){
33824         this.size = this.el.getSize();
33825         this.xy = this.el.getXY();
33826         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33827     },
33828
33829     // private
33830     // z-index is managed by the DialogManager and may be overwritten at any time
33831     setZIndex : function(index){
33832         if(this.modal){
33833             this.mask.setStyle("z-index", index);
33834         }
33835         if(this.shim){
33836             this.shim.setStyle("z-index", ++index);
33837         }
33838         if(this.shadow){
33839             this.shadow.setZIndex(++index);
33840         }
33841         this.el.setStyle("z-index", ++index);
33842         if(this.proxy){
33843             this.proxy.setStyle("z-index", ++index);
33844         }
33845         if(this.resizer){
33846             this.resizer.proxy.setStyle("z-index", ++index);
33847         }
33848
33849         this.lastZIndex = index;
33850     },
33851
33852     /**
33853      * Returns the element for this dialog
33854      * @return {Roo.Element} The underlying dialog Element
33855      */
33856     getEl : function(){
33857         return this.el;
33858     }
33859 });
33860
33861 /**
33862  * @class Roo.DialogManager
33863  * Provides global access to BasicDialogs that have been created and
33864  * support for z-indexing (layering) multiple open dialogs.
33865  */
33866 Roo.DialogManager = function(){
33867     var list = {};
33868     var accessList = [];
33869     var front = null;
33870
33871     // private
33872     var sortDialogs = function(d1, d2){
33873         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33874     };
33875
33876     // private
33877     var orderDialogs = function(){
33878         accessList.sort(sortDialogs);
33879         var seed = Roo.DialogManager.zseed;
33880         for(var i = 0, len = accessList.length; i < len; i++){
33881             var dlg = accessList[i];
33882             if(dlg){
33883                 dlg.setZIndex(seed + (i*10));
33884             }
33885         }
33886     };
33887
33888     return {
33889         /**
33890          * The starting z-index for BasicDialogs (defaults to 9000)
33891          * @type Number The z-index value
33892          */
33893         zseed : 9000,
33894
33895         // private
33896         register : function(dlg){
33897             list[dlg.id] = dlg;
33898             accessList.push(dlg);
33899         },
33900
33901         // private
33902         unregister : function(dlg){
33903             delete list[dlg.id];
33904             var i=0;
33905             var len=0;
33906             if(!accessList.indexOf){
33907                 for(  i = 0, len = accessList.length; i < len; i++){
33908                     if(accessList[i] == dlg){
33909                         accessList.splice(i, 1);
33910                         return;
33911                     }
33912                 }
33913             }else{
33914                  i = accessList.indexOf(dlg);
33915                 if(i != -1){
33916                     accessList.splice(i, 1);
33917                 }
33918             }
33919         },
33920
33921         /**
33922          * Gets a registered dialog by id
33923          * @param {String/Object} id The id of the dialog or a dialog
33924          * @return {Roo.BasicDialog} this
33925          */
33926         get : function(id){
33927             return typeof id == "object" ? id : list[id];
33928         },
33929
33930         /**
33931          * Brings the specified dialog to the front
33932          * @param {String/Object} dlg The id of the dialog or a dialog
33933          * @return {Roo.BasicDialog} this
33934          */
33935         bringToFront : function(dlg){
33936             dlg = this.get(dlg);
33937             if(dlg != front){
33938                 front = dlg;
33939                 dlg._lastAccess = new Date().getTime();
33940                 orderDialogs();
33941             }
33942             return dlg;
33943         },
33944
33945         /**
33946          * Sends the specified dialog to the back
33947          * @param {String/Object} dlg The id of the dialog or a dialog
33948          * @return {Roo.BasicDialog} this
33949          */
33950         sendToBack : function(dlg){
33951             dlg = this.get(dlg);
33952             dlg._lastAccess = -(new Date().getTime());
33953             orderDialogs();
33954             return dlg;
33955         },
33956
33957         /**
33958          * Hides all dialogs
33959          */
33960         hideAll : function(){
33961             for(var id in list){
33962                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33963                     list[id].hide();
33964                 }
33965             }
33966         }
33967     };
33968 }();
33969
33970 /**
33971  * @class Roo.LayoutDialog
33972  * @extends Roo.BasicDialog
33973  * @children Roo.ContentPanel
33974  * @parent builder none
33975  * Dialog which provides adjustments for working with a layout in a Dialog.
33976  * Add your necessary layout config options to the dialog's config.<br>
33977  * Example usage (including a nested layout):
33978  * <pre><code>
33979 if(!dialog){
33980     dialog = new Roo.LayoutDialog("download-dlg", {
33981         modal: true,
33982         width:600,
33983         height:450,
33984         shadow:true,
33985         minWidth:500,
33986         minHeight:350,
33987         autoTabs:true,
33988         proxyDrag:true,
33989         // layout config merges with the dialog config
33990         center:{
33991             tabPosition: "top",
33992             alwaysShowTabs: true
33993         }
33994     });
33995     dialog.addKeyListener(27, dialog.hide, dialog);
33996     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33997     dialog.addButton("Build It!", this.getDownload, this);
33998
33999     // we can even add nested layouts
34000     var innerLayout = new Roo.BorderLayout("dl-inner", {
34001         east: {
34002             initialSize: 200,
34003             autoScroll:true,
34004             split:true
34005         },
34006         center: {
34007             autoScroll:true
34008         }
34009     });
34010     innerLayout.beginUpdate();
34011     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34012     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34013     innerLayout.endUpdate(true);
34014
34015     var layout = dialog.getLayout();
34016     layout.beginUpdate();
34017     layout.add("center", new Roo.ContentPanel("standard-panel",
34018                         {title: "Download the Source", fitToFrame:true}));
34019     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34020                {title: "Build your own roo.js"}));
34021     layout.getRegion("center").showPanel(sp);
34022     layout.endUpdate();
34023 }
34024 </code></pre>
34025     * @constructor
34026     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34027     * @param {Object} config configuration options
34028   */
34029 Roo.LayoutDialog = function(el, cfg){
34030     
34031     var config=  cfg;
34032     if (typeof(cfg) == 'undefined') {
34033         config = Roo.apply({}, el);
34034         // not sure why we use documentElement here.. - it should always be body.
34035         // IE7 borks horribly if we use documentElement.
34036         // webkit also does not like documentElement - it creates a body element...
34037         el = Roo.get( document.body || document.documentElement ).createChild();
34038         //config.autoCreate = true;
34039     }
34040     
34041     
34042     config.autoTabs = false;
34043     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34044     this.body.setStyle({overflow:"hidden", position:"relative"});
34045     this.layout = new Roo.BorderLayout(this.body.dom, config);
34046     this.layout.monitorWindowResize = false;
34047     this.el.addClass("x-dlg-auto-layout");
34048     // fix case when center region overwrites center function
34049     this.center = Roo.BasicDialog.prototype.center;
34050     this.on("show", this.layout.layout, this.layout, true);
34051     if (config.items) {
34052         var xitems = config.items;
34053         delete config.items;
34054         Roo.each(xitems, this.addxtype, this);
34055     }
34056     
34057     
34058 };
34059 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34060     
34061     
34062     /**
34063      * @cfg {Roo.LayoutRegion} east  
34064      */
34065     /**
34066      * @cfg {Roo.LayoutRegion} west
34067      */
34068     /**
34069      * @cfg {Roo.LayoutRegion} south
34070      */
34071     /**
34072      * @cfg {Roo.LayoutRegion} north
34073      */
34074     /**
34075      * @cfg {Roo.LayoutRegion} center
34076      */
34077     /**
34078      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34079      */
34080     
34081     
34082     /**
34083      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34084      * @deprecated
34085      */
34086     endUpdate : function(){
34087         this.layout.endUpdate();
34088     },
34089
34090     /**
34091      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34092      *  @deprecated
34093      */
34094     beginUpdate : function(){
34095         this.layout.beginUpdate();
34096     },
34097
34098     /**
34099      * Get the BorderLayout for this dialog
34100      * @return {Roo.BorderLayout}
34101      */
34102     getLayout : function(){
34103         return this.layout;
34104     },
34105
34106     showEl : function(){
34107         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34108         if(Roo.isIE7){
34109             this.layout.layout();
34110         }
34111     },
34112
34113     // private
34114     // Use the syncHeightBeforeShow config option to control this automatically
34115     syncBodyHeight : function(){
34116         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34117         if(this.layout){this.layout.layout();}
34118     },
34119     
34120       /**
34121      * Add an xtype element (actually adds to the layout.)
34122      * @return {Object} xdata xtype object data.
34123      */
34124     
34125     addxtype : function(c) {
34126         return this.layout.addxtype(c);
34127     }
34128 });/*
34129  * Based on:
34130  * Ext JS Library 1.1.1
34131  * Copyright(c) 2006-2007, Ext JS, LLC.
34132  *
34133  * Originally Released Under LGPL - original licence link has changed is not relivant.
34134  *
34135  * Fork - LGPL
34136  * <script type="text/javascript">
34137  */
34138  
34139 /**
34140  * @class Roo.MessageBox
34141  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34142  * Example usage:
34143  *<pre><code>
34144 // Basic alert:
34145 Roo.Msg.alert('Status', 'Changes saved successfully.');
34146
34147 // Prompt for user data:
34148 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34149     if (btn == 'ok'){
34150         // process text value...
34151     }
34152 });
34153
34154 // Show a dialog using config options:
34155 Roo.Msg.show({
34156    title:'Save Changes?',
34157    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34158    buttons: Roo.Msg.YESNOCANCEL,
34159    fn: processResult,
34160    animEl: 'elId'
34161 });
34162 </code></pre>
34163  * @static
34164  */
34165 Roo.MessageBox = function(){
34166     var dlg, opt, mask, waitTimer;
34167     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34168     var buttons, activeTextEl, bwidth;
34169
34170     // private
34171     var handleButton = function(button){
34172         dlg.hide();
34173         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34174     };
34175
34176     // private
34177     var handleHide = function(){
34178         if(opt && opt.cls){
34179             dlg.el.removeClass(opt.cls);
34180         }
34181         if(waitTimer){
34182             Roo.TaskMgr.stop(waitTimer);
34183             waitTimer = null;
34184         }
34185     };
34186
34187     // private
34188     var updateButtons = function(b){
34189         var width = 0;
34190         if(!b){
34191             buttons["ok"].hide();
34192             buttons["cancel"].hide();
34193             buttons["yes"].hide();
34194             buttons["no"].hide();
34195             dlg.footer.dom.style.display = 'none';
34196             return width;
34197         }
34198         dlg.footer.dom.style.display = '';
34199         for(var k in buttons){
34200             if(typeof buttons[k] != "function"){
34201                 if(b[k]){
34202                     buttons[k].show();
34203                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34204                     width += buttons[k].el.getWidth()+15;
34205                 }else{
34206                     buttons[k].hide();
34207                 }
34208             }
34209         }
34210         return width;
34211     };
34212
34213     // private
34214     var handleEsc = function(d, k, e){
34215         if(opt && opt.closable !== false){
34216             dlg.hide();
34217         }
34218         if(e){
34219             e.stopEvent();
34220         }
34221     };
34222
34223     return {
34224         /**
34225          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34226          * @return {Roo.BasicDialog} The BasicDialog element
34227          */
34228         getDialog : function(){
34229            if(!dlg){
34230                 dlg = new Roo.BasicDialog("x-msg-box", {
34231                     autoCreate : true,
34232                     shadow: true,
34233                     draggable: true,
34234                     resizable:false,
34235                     constraintoviewport:false,
34236                     fixedcenter:true,
34237                     collapsible : false,
34238                     shim:true,
34239                     modal: true,
34240                     width:400, height:100,
34241                     buttonAlign:"center",
34242                     closeClick : function(){
34243                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34244                             handleButton("no");
34245                         }else{
34246                             handleButton("cancel");
34247                         }
34248                     }
34249                 });
34250                 dlg.on("hide", handleHide);
34251                 mask = dlg.mask;
34252                 dlg.addKeyListener(27, handleEsc);
34253                 buttons = {};
34254                 var bt = this.buttonText;
34255                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34256                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34257                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34258                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34259                 bodyEl = dlg.body.createChild({
34260
34261                     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>'
34262                 });
34263                 msgEl = bodyEl.dom.firstChild;
34264                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34265                 textboxEl.enableDisplayMode();
34266                 textboxEl.addKeyListener([10,13], function(){
34267                     if(dlg.isVisible() && opt && opt.buttons){
34268                         if(opt.buttons.ok){
34269                             handleButton("ok");
34270                         }else if(opt.buttons.yes){
34271                             handleButton("yes");
34272                         }
34273                     }
34274                 });
34275                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34276                 textareaEl.enableDisplayMode();
34277                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34278                 progressEl.enableDisplayMode();
34279                 var pf = progressEl.dom.firstChild;
34280                 if (pf) {
34281                     pp = Roo.get(pf.firstChild);
34282                     pp.setHeight(pf.offsetHeight);
34283                 }
34284                 
34285             }
34286             return dlg;
34287         },
34288
34289         /**
34290          * Updates the message box body text
34291          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34292          * the XHTML-compliant non-breaking space character '&amp;#160;')
34293          * @return {Roo.MessageBox} This message box
34294          */
34295         updateText : function(text){
34296             if(!dlg.isVisible() && !opt.width){
34297                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34298             }
34299             msgEl.innerHTML = text || '&#160;';
34300       
34301             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34302             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34303             var w = Math.max(
34304                     Math.min(opt.width || cw , this.maxWidth), 
34305                     Math.max(opt.minWidth || this.minWidth, bwidth)
34306             );
34307             if(opt.prompt){
34308                 activeTextEl.setWidth(w);
34309             }
34310             if(dlg.isVisible()){
34311                 dlg.fixedcenter = false;
34312             }
34313             // to big, make it scroll. = But as usual stupid IE does not support
34314             // !important..
34315             
34316             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34317                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34318                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34319             } else {
34320                 bodyEl.dom.style.height = '';
34321                 bodyEl.dom.style.overflowY = '';
34322             }
34323             if (cw > w) {
34324                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34325             } else {
34326                 bodyEl.dom.style.overflowX = '';
34327             }
34328             
34329             dlg.setContentSize(w, bodyEl.getHeight());
34330             if(dlg.isVisible()){
34331                 dlg.fixedcenter = true;
34332             }
34333             return this;
34334         },
34335
34336         /**
34337          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34338          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34339          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34340          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34341          * @return {Roo.MessageBox} This message box
34342          */
34343         updateProgress : function(value, text){
34344             if(text){
34345                 this.updateText(text);
34346             }
34347             if (pp) { // weird bug on my firefox - for some reason this is not defined
34348                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34349             }
34350             return this;
34351         },        
34352
34353         /**
34354          * Returns true if the message box is currently displayed
34355          * @return {Boolean} True if the message box is visible, else false
34356          */
34357         isVisible : function(){
34358             return dlg && dlg.isVisible();  
34359         },
34360
34361         /**
34362          * Hides the message box if it is displayed
34363          */
34364         hide : function(){
34365             if(this.isVisible()){
34366                 dlg.hide();
34367             }  
34368         },
34369
34370         /**
34371          * Displays a new message box, or reinitializes an existing message box, based on the config options
34372          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34373          * The following config object properties are supported:
34374          * <pre>
34375 Property    Type             Description
34376 ----------  ---------------  ------------------------------------------------------------------------------------
34377 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34378                                    closes (defaults to undefined)
34379 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34380                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34381 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34382                                    progress and wait dialogs will ignore this property and always hide the
34383                                    close button as they can only be closed programmatically.
34384 cls               String           A custom CSS class to apply to the message box element
34385 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34386                                    displayed (defaults to 75)
34387 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34388                                    function will be btn (the name of the button that was clicked, if applicable,
34389                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34390                                    Progress and wait dialogs will ignore this option since they do not respond to
34391                                    user actions and can only be closed programmatically, so any required function
34392                                    should be called by the same code after it closes the dialog.
34393 icon              String           A CSS class that provides a background image to be used as an icon for
34394                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34395 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34396 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34397 modal             Boolean          False to allow user interaction with the page while the message box is
34398                                    displayed (defaults to true)
34399 msg               String           A string that will replace the existing message box body text (defaults
34400                                    to the XHTML-compliant non-breaking space character '&#160;')
34401 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34402 progress          Boolean          True to display a progress bar (defaults to false)
34403 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34404 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34405 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34406 title             String           The title text
34407 value             String           The string value to set into the active textbox element if displayed
34408 wait              Boolean          True to display a progress bar (defaults to false)
34409 width             Number           The width of the dialog in pixels
34410 </pre>
34411          *
34412          * Example usage:
34413          * <pre><code>
34414 Roo.Msg.show({
34415    title: 'Address',
34416    msg: 'Please enter your address:',
34417    width: 300,
34418    buttons: Roo.MessageBox.OKCANCEL,
34419    multiline: true,
34420    fn: saveAddress,
34421    animEl: 'addAddressBtn'
34422 });
34423 </code></pre>
34424          * @param {Object} config Configuration options
34425          * @return {Roo.MessageBox} This message box
34426          */
34427         show : function(options)
34428         {
34429             
34430             // this causes nightmares if you show one dialog after another
34431             // especially on callbacks..
34432              
34433             if(this.isVisible()){
34434                 
34435                 this.hide();
34436                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34437                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34438                 Roo.log("New Dialog Message:" +  options.msg )
34439                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34440                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34441                 
34442             }
34443             var d = this.getDialog();
34444             opt = options;
34445             d.setTitle(opt.title || "&#160;");
34446             d.close.setDisplayed(opt.closable !== false);
34447             activeTextEl = textboxEl;
34448             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34449             if(opt.prompt){
34450                 if(opt.multiline){
34451                     textboxEl.hide();
34452                     textareaEl.show();
34453                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34454                         opt.multiline : this.defaultTextHeight);
34455                     activeTextEl = textareaEl;
34456                 }else{
34457                     textboxEl.show();
34458                     textareaEl.hide();
34459                 }
34460             }else{
34461                 textboxEl.hide();
34462                 textareaEl.hide();
34463             }
34464             progressEl.setDisplayed(opt.progress === true);
34465             this.updateProgress(0);
34466             activeTextEl.dom.value = opt.value || "";
34467             if(opt.prompt){
34468                 dlg.setDefaultButton(activeTextEl);
34469             }else{
34470                 var bs = opt.buttons;
34471                 var db = null;
34472                 if(bs && bs.ok){
34473                     db = buttons["ok"];
34474                 }else if(bs && bs.yes){
34475                     db = buttons["yes"];
34476                 }
34477                 dlg.setDefaultButton(db);
34478             }
34479             bwidth = updateButtons(opt.buttons);
34480             this.updateText(opt.msg);
34481             if(opt.cls){
34482                 d.el.addClass(opt.cls);
34483             }
34484             d.proxyDrag = opt.proxyDrag === true;
34485             d.modal = opt.modal !== false;
34486             d.mask = opt.modal !== false ? mask : false;
34487             if(!d.isVisible()){
34488                 // force it to the end of the z-index stack so it gets a cursor in FF
34489                 document.body.appendChild(dlg.el.dom);
34490                 d.animateTarget = null;
34491                 d.show(options.animEl);
34492             }
34493             return this;
34494         },
34495
34496         /**
34497          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34498          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34499          * and closing the message box when the process is complete.
34500          * @param {String} title The title bar text
34501          * @param {String} msg The message box body text
34502          * @return {Roo.MessageBox} This message box
34503          */
34504         progress : function(title, msg){
34505             this.show({
34506                 title : title,
34507                 msg : msg,
34508                 buttons: false,
34509                 progress:true,
34510                 closable:false,
34511                 minWidth: this.minProgressWidth,
34512                 modal : true
34513             });
34514             return this;
34515         },
34516
34517         /**
34518          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34519          * If a callback function is passed it will be called after the user clicks the button, and the
34520          * id of the button that was clicked will be passed as the only parameter to the callback
34521          * (could also be the top-right close button).
34522          * @param {String} title The title bar text
34523          * @param {String} msg The message box body text
34524          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34525          * @param {Object} scope (optional) The scope of the callback function
34526          * @return {Roo.MessageBox} This message box
34527          */
34528         alert : function(title, msg, fn, scope){
34529             this.show({
34530                 title : title,
34531                 msg : msg,
34532                 buttons: this.OK,
34533                 fn: fn,
34534                 scope : scope,
34535                 modal : true
34536             });
34537             return this;
34538         },
34539
34540         /**
34541          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34542          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34543          * You are responsible for closing the message box when the process is complete.
34544          * @param {String} msg The message box body text
34545          * @param {String} title (optional) The title bar text
34546          * @return {Roo.MessageBox} This message box
34547          */
34548         wait : function(msg, title){
34549             this.show({
34550                 title : title,
34551                 msg : msg,
34552                 buttons: false,
34553                 closable:false,
34554                 progress:true,
34555                 modal:true,
34556                 width:300,
34557                 wait:true
34558             });
34559             waitTimer = Roo.TaskMgr.start({
34560                 run: function(i){
34561                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34562                 },
34563                 interval: 1000
34564             });
34565             return this;
34566         },
34567
34568         /**
34569          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34570          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34571          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34572          * @param {String} title The title bar text
34573          * @param {String} msg The message box body text
34574          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34575          * @param {Object} scope (optional) The scope of the callback function
34576          * @return {Roo.MessageBox} This message box
34577          */
34578         confirm : function(title, msg, fn, scope){
34579             this.show({
34580                 title : title,
34581                 msg : msg,
34582                 buttons: this.YESNO,
34583                 fn: fn,
34584                 scope : scope,
34585                 modal : true
34586             });
34587             return this;
34588         },
34589
34590         /**
34591          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34592          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34593          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34594          * (could also be the top-right close button) and the text that was entered will be passed as the two
34595          * parameters to the callback.
34596          * @param {String} title The title bar text
34597          * @param {String} msg The message box body text
34598          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34599          * @param {Object} scope (optional) The scope of the callback function
34600          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34601          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34602          * @return {Roo.MessageBox} This message box
34603          */
34604         prompt : function(title, msg, fn, scope, multiline){
34605             this.show({
34606                 title : title,
34607                 msg : msg,
34608                 buttons: this.OKCANCEL,
34609                 fn: fn,
34610                 minWidth:250,
34611                 scope : scope,
34612                 prompt:true,
34613                 multiline: multiline,
34614                 modal : true
34615             });
34616             return this;
34617         },
34618
34619         /**
34620          * Button config that displays a single OK button
34621          * @type Object
34622          */
34623         OK : {ok:true},
34624         /**
34625          * Button config that displays Yes and No buttons
34626          * @type Object
34627          */
34628         YESNO : {yes:true, no:true},
34629         /**
34630          * Button config that displays OK and Cancel buttons
34631          * @type Object
34632          */
34633         OKCANCEL : {ok:true, cancel:true},
34634         /**
34635          * Button config that displays Yes, No and Cancel buttons
34636          * @type Object
34637          */
34638         YESNOCANCEL : {yes:true, no:true, cancel:true},
34639
34640         /**
34641          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34642          * @type Number
34643          */
34644         defaultTextHeight : 75,
34645         /**
34646          * The maximum width in pixels of the message box (defaults to 600)
34647          * @type Number
34648          */
34649         maxWidth : 600,
34650         /**
34651          * The minimum width in pixels of the message box (defaults to 100)
34652          * @type Number
34653          */
34654         minWidth : 100,
34655         /**
34656          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34657          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34658          * @type Number
34659          */
34660         minProgressWidth : 250,
34661         /**
34662          * An object containing the default button text strings that can be overriden for localized language support.
34663          * Supported properties are: ok, cancel, yes and no.
34664          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34665          * @type Object
34666          */
34667         buttonText : {
34668             ok : "OK",
34669             cancel : "Cancel",
34670             yes : "Yes",
34671             no : "No"
34672         }
34673     };
34674 }();
34675
34676 /**
34677  * Shorthand for {@link Roo.MessageBox}
34678  */
34679 Roo.Msg = Roo.MessageBox;/*
34680  * Based on:
34681  * Ext JS Library 1.1.1
34682  * Copyright(c) 2006-2007, Ext JS, LLC.
34683  *
34684  * Originally Released Under LGPL - original licence link has changed is not relivant.
34685  *
34686  * Fork - LGPL
34687  * <script type="text/javascript">
34688  */
34689 /**
34690  * @class Roo.QuickTips
34691  * Provides attractive and customizable tooltips for any element.
34692  * @static
34693  */
34694 Roo.QuickTips = function(){
34695     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34696     var ce, bd, xy, dd;
34697     var visible = false, disabled = true, inited = false;
34698     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34699     
34700     var onOver = function(e){
34701         if(disabled){
34702             return;
34703         }
34704         var t = e.getTarget();
34705         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34706             return;
34707         }
34708         if(ce && t == ce.el){
34709             clearTimeout(hideProc);
34710             return;
34711         }
34712         if(t && tagEls[t.id]){
34713             tagEls[t.id].el = t;
34714             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34715             return;
34716         }
34717         var ttp, et = Roo.fly(t);
34718         var ns = cfg.namespace;
34719         if(tm.interceptTitles && t.title){
34720             ttp = t.title;
34721             t.qtip = ttp;
34722             t.removeAttribute("title");
34723             e.preventDefault();
34724         }else{
34725             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34726         }
34727         if(ttp){
34728             showProc = show.defer(tm.showDelay, tm, [{
34729                 el: t, 
34730                 text: ttp.replace(/\\n/g,'<br/>'),
34731                 width: et.getAttributeNS(ns, cfg.width),
34732                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34733                 title: et.getAttributeNS(ns, cfg.title),
34734                     cls: et.getAttributeNS(ns, cfg.cls)
34735             }]);
34736         }
34737     };
34738     
34739     var onOut = function(e){
34740         clearTimeout(showProc);
34741         var t = e.getTarget();
34742         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34743             hideProc = setTimeout(hide, tm.hideDelay);
34744         }
34745     };
34746     
34747     var onMove = function(e){
34748         if(disabled){
34749             return;
34750         }
34751         xy = e.getXY();
34752         xy[1] += 18;
34753         if(tm.trackMouse && ce){
34754             el.setXY(xy);
34755         }
34756     };
34757     
34758     var onDown = function(e){
34759         clearTimeout(showProc);
34760         clearTimeout(hideProc);
34761         if(!e.within(el)){
34762             if(tm.hideOnClick){
34763                 hide();
34764                 tm.disable();
34765                 tm.enable.defer(100, tm);
34766             }
34767         }
34768     };
34769     
34770     var getPad = function(){
34771         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34772     };
34773
34774     var show = function(o){
34775         if(disabled){
34776             return;
34777         }
34778         clearTimeout(dismissProc);
34779         ce = o;
34780         if(removeCls){ // in case manually hidden
34781             el.removeClass(removeCls);
34782             removeCls = null;
34783         }
34784         if(ce.cls){
34785             el.addClass(ce.cls);
34786             removeCls = ce.cls;
34787         }
34788         if(ce.title){
34789             tipTitle.update(ce.title);
34790             tipTitle.show();
34791         }else{
34792             tipTitle.update('');
34793             tipTitle.hide();
34794         }
34795         el.dom.style.width  = tm.maxWidth+'px';
34796         //tipBody.dom.style.width = '';
34797         tipBodyText.update(o.text);
34798         var p = getPad(), w = ce.width;
34799         if(!w){
34800             var td = tipBodyText.dom;
34801             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34802             if(aw > tm.maxWidth){
34803                 w = tm.maxWidth;
34804             }else if(aw < tm.minWidth){
34805                 w = tm.minWidth;
34806             }else{
34807                 w = aw;
34808             }
34809         }
34810         //tipBody.setWidth(w);
34811         el.setWidth(parseInt(w, 10) + p);
34812         if(ce.autoHide === false){
34813             close.setDisplayed(true);
34814             if(dd){
34815                 dd.unlock();
34816             }
34817         }else{
34818             close.setDisplayed(false);
34819             if(dd){
34820                 dd.lock();
34821             }
34822         }
34823         if(xy){
34824             el.avoidY = xy[1]-18;
34825             el.setXY(xy);
34826         }
34827         if(tm.animate){
34828             el.setOpacity(.1);
34829             el.setStyle("visibility", "visible");
34830             el.fadeIn({callback: afterShow});
34831         }else{
34832             afterShow();
34833         }
34834     };
34835     
34836     var afterShow = function(){
34837         if(ce){
34838             el.show();
34839             esc.enable();
34840             if(tm.autoDismiss && ce.autoHide !== false){
34841                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34842             }
34843         }
34844     };
34845     
34846     var hide = function(noanim){
34847         clearTimeout(dismissProc);
34848         clearTimeout(hideProc);
34849         ce = null;
34850         if(el.isVisible()){
34851             esc.disable();
34852             if(noanim !== true && tm.animate){
34853                 el.fadeOut({callback: afterHide});
34854             }else{
34855                 afterHide();
34856             } 
34857         }
34858     };
34859     
34860     var afterHide = function(){
34861         el.hide();
34862         if(removeCls){
34863             el.removeClass(removeCls);
34864             removeCls = null;
34865         }
34866     };
34867     
34868     return {
34869         /**
34870         * @cfg {Number} minWidth
34871         * The minimum width of the quick tip (defaults to 40)
34872         */
34873        minWidth : 40,
34874         /**
34875         * @cfg {Number} maxWidth
34876         * The maximum width of the quick tip (defaults to 300)
34877         */
34878        maxWidth : 300,
34879         /**
34880         * @cfg {Boolean} interceptTitles
34881         * True to automatically use the element's DOM title value if available (defaults to false)
34882         */
34883        interceptTitles : false,
34884         /**
34885         * @cfg {Boolean} trackMouse
34886         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34887         */
34888        trackMouse : false,
34889         /**
34890         * @cfg {Boolean} hideOnClick
34891         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34892         */
34893        hideOnClick : true,
34894         /**
34895         * @cfg {Number} showDelay
34896         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34897         */
34898        showDelay : 500,
34899         /**
34900         * @cfg {Number} hideDelay
34901         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34902         */
34903        hideDelay : 200,
34904         /**
34905         * @cfg {Boolean} autoHide
34906         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34907         * Used in conjunction with hideDelay.
34908         */
34909        autoHide : true,
34910         /**
34911         * @cfg {Boolean}
34912         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34913         * (defaults to true).  Used in conjunction with autoDismissDelay.
34914         */
34915        autoDismiss : true,
34916         /**
34917         * @cfg {Number}
34918         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34919         */
34920        autoDismissDelay : 5000,
34921        /**
34922         * @cfg {Boolean} animate
34923         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34924         */
34925        animate : false,
34926
34927        /**
34928         * @cfg {String} title
34929         * Title text to display (defaults to '').  This can be any valid HTML markup.
34930         */
34931         title: '',
34932        /**
34933         * @cfg {String} text
34934         * Body text to display (defaults to '').  This can be any valid HTML markup.
34935         */
34936         text : '',
34937        /**
34938         * @cfg {String} cls
34939         * A CSS class to apply to the base quick tip element (defaults to '').
34940         */
34941         cls : '',
34942        /**
34943         * @cfg {Number} width
34944         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34945         * minWidth or maxWidth.
34946         */
34947         width : null,
34948
34949     /**
34950      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34951      * or display QuickTips in a page.
34952      */
34953        init : function(){
34954           tm = Roo.QuickTips;
34955           cfg = tm.tagConfig;
34956           if(!inited){
34957               if(!Roo.isReady){ // allow calling of init() before onReady
34958                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34959                   return;
34960               }
34961               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34962               el.fxDefaults = {stopFx: true};
34963               // maximum custom styling
34964               //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>');
34965               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>');              
34966               tipTitle = el.child('h3');
34967               tipTitle.enableDisplayMode("block");
34968               tipBody = el.child('div.x-tip-bd');
34969               tipBodyText = el.child('div.x-tip-bd-inner');
34970               //bdLeft = el.child('div.x-tip-bd-left');
34971               //bdRight = el.child('div.x-tip-bd-right');
34972               close = el.child('div.x-tip-close');
34973               close.enableDisplayMode("block");
34974               close.on("click", hide);
34975               var d = Roo.get(document);
34976               d.on("mousedown", onDown);
34977               d.on("mouseover", onOver);
34978               d.on("mouseout", onOut);
34979               d.on("mousemove", onMove);
34980               esc = d.addKeyListener(27, hide);
34981               esc.disable();
34982               if(Roo.dd.DD){
34983                   dd = el.initDD("default", null, {
34984                       onDrag : function(){
34985                           el.sync();  
34986                       }
34987                   });
34988                   dd.setHandleElId(tipTitle.id);
34989                   dd.lock();
34990               }
34991               inited = true;
34992           }
34993           this.enable(); 
34994        },
34995
34996     /**
34997      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34998      * are supported:
34999      * <pre>
35000 Property    Type                   Description
35001 ----------  ---------------------  ------------------------------------------------------------------------
35002 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35003      * </ul>
35004      * @param {Object} config The config object
35005      */
35006        register : function(config){
35007            var cs = config instanceof Array ? config : arguments;
35008            for(var i = 0, len = cs.length; i < len; i++) {
35009                var c = cs[i];
35010                var target = c.target;
35011                if(target){
35012                    if(target instanceof Array){
35013                        for(var j = 0, jlen = target.length; j < jlen; j++){
35014                            tagEls[target[j]] = c;
35015                        }
35016                    }else{
35017                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35018                    }
35019                }
35020            }
35021        },
35022
35023     /**
35024      * Removes this quick tip from its element and destroys it.
35025      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35026      */
35027        unregister : function(el){
35028            delete tagEls[Roo.id(el)];
35029        },
35030
35031     /**
35032      * Enable this quick tip.
35033      */
35034        enable : function(){
35035            if(inited && disabled){
35036                locks.pop();
35037                if(locks.length < 1){
35038                    disabled = false;
35039                }
35040            }
35041        },
35042
35043     /**
35044      * Disable this quick tip.
35045      */
35046        disable : function(){
35047           disabled = true;
35048           clearTimeout(showProc);
35049           clearTimeout(hideProc);
35050           clearTimeout(dismissProc);
35051           if(ce){
35052               hide(true);
35053           }
35054           locks.push(1);
35055        },
35056
35057     /**
35058      * Returns true if the quick tip is enabled, else false.
35059      */
35060        isEnabled : function(){
35061             return !disabled;
35062        },
35063
35064         // private
35065        tagConfig : {
35066            namespace : "roo", // was ext?? this may break..
35067            alt_namespace : "ext",
35068            attribute : "qtip",
35069            width : "width",
35070            target : "target",
35071            title : "qtitle",
35072            hide : "hide",
35073            cls : "qclass"
35074        }
35075    };
35076 }();
35077
35078 // backwards compat
35079 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35080  * Based on:
35081  * Ext JS Library 1.1.1
35082  * Copyright(c) 2006-2007, Ext JS, LLC.
35083  *
35084  * Originally Released Under LGPL - original licence link has changed is not relivant.
35085  *
35086  * Fork - LGPL
35087  * <script type="text/javascript">
35088  */
35089  
35090
35091 /**
35092  * @class Roo.tree.TreePanel
35093  * @extends Roo.data.Tree
35094  * @cfg {Roo.tree.TreeNode} root The root node
35095  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35096  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35097  * @cfg {Boolean} enableDD true to enable drag and drop
35098  * @cfg {Boolean} enableDrag true to enable just drag
35099  * @cfg {Boolean} enableDrop true to enable just drop
35100  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35101  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35102  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35103  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35104  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35105  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35106  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35107  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35108  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35109  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35110  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35111  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35112  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35113  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35114  * @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>
35115  * @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>
35116  * 
35117  * @constructor
35118  * @param {String/HTMLElement/Element} el The container element
35119  * @param {Object} config
35120  */
35121 Roo.tree.TreePanel = function(el, config){
35122     var root = false;
35123     var loader = false;
35124     if (config.root) {
35125         root = config.root;
35126         delete config.root;
35127     }
35128     if (config.loader) {
35129         loader = config.loader;
35130         delete config.loader;
35131     }
35132     
35133     Roo.apply(this, config);
35134     Roo.tree.TreePanel.superclass.constructor.call(this);
35135     this.el = Roo.get(el);
35136     this.el.addClass('x-tree');
35137     //console.log(root);
35138     if (root) {
35139         this.setRootNode( Roo.factory(root, Roo.tree));
35140     }
35141     if (loader) {
35142         this.loader = Roo.factory(loader, Roo.tree);
35143     }
35144    /**
35145     * Read-only. The id of the container element becomes this TreePanel's id.
35146     */
35147     this.id = this.el.id;
35148     this.addEvents({
35149         /**
35150         * @event beforeload
35151         * Fires before a node is loaded, return false to cancel
35152         * @param {Node} node The node being loaded
35153         */
35154         "beforeload" : true,
35155         /**
35156         * @event load
35157         * Fires when a node is loaded
35158         * @param {Node} node The node that was loaded
35159         */
35160         "load" : true,
35161         /**
35162         * @event textchange
35163         * Fires when the text for a node is changed
35164         * @param {Node} node The node
35165         * @param {String} text The new text
35166         * @param {String} oldText The old text
35167         */
35168         "textchange" : true,
35169         /**
35170         * @event beforeexpand
35171         * Fires before a node is expanded, return false to cancel.
35172         * @param {Node} node The node
35173         * @param {Boolean} deep
35174         * @param {Boolean} anim
35175         */
35176         "beforeexpand" : true,
35177         /**
35178         * @event beforecollapse
35179         * Fires before a node is collapsed, return false to cancel.
35180         * @param {Node} node The node
35181         * @param {Boolean} deep
35182         * @param {Boolean} anim
35183         */
35184         "beforecollapse" : true,
35185         /**
35186         * @event expand
35187         * Fires when a node is expanded
35188         * @param {Node} node The node
35189         */
35190         "expand" : true,
35191         /**
35192         * @event disabledchange
35193         * Fires when the disabled status of a node changes
35194         * @param {Node} node The node
35195         * @param {Boolean} disabled
35196         */
35197         "disabledchange" : true,
35198         /**
35199         * @event collapse
35200         * Fires when a node is collapsed
35201         * @param {Node} node The node
35202         */
35203         "collapse" : true,
35204         /**
35205         * @event beforeclick
35206         * Fires before click processing on a node. Return false to cancel the default action.
35207         * @param {Node} node The node
35208         * @param {Roo.EventObject} e The event object
35209         */
35210         "beforeclick":true,
35211         /**
35212         * @event checkchange
35213         * Fires when a node with a checkbox's checked property changes
35214         * @param {Node} this This node
35215         * @param {Boolean} checked
35216         */
35217         "checkchange":true,
35218         /**
35219         * @event click
35220         * Fires when a node is clicked
35221         * @param {Node} node The node
35222         * @param {Roo.EventObject} e The event object
35223         */
35224         "click":true,
35225         /**
35226         * @event dblclick
35227         * Fires when a node is double clicked
35228         * @param {Node} node The node
35229         * @param {Roo.EventObject} e The event object
35230         */
35231         "dblclick":true,
35232         /**
35233         * @event contextmenu
35234         * Fires when a node is right clicked
35235         * @param {Node} node The node
35236         * @param {Roo.EventObject} e The event object
35237         */
35238         "contextmenu":true,
35239         /**
35240         * @event beforechildrenrendered
35241         * Fires right before the child nodes for a node are rendered
35242         * @param {Node} node The node
35243         */
35244         "beforechildrenrendered":true,
35245         /**
35246         * @event startdrag
35247         * Fires when a node starts being dragged
35248         * @param {Roo.tree.TreePanel} this
35249         * @param {Roo.tree.TreeNode} node
35250         * @param {event} e The raw browser event
35251         */ 
35252        "startdrag" : true,
35253        /**
35254         * @event enddrag
35255         * Fires when a drag operation is complete
35256         * @param {Roo.tree.TreePanel} this
35257         * @param {Roo.tree.TreeNode} node
35258         * @param {event} e The raw browser event
35259         */
35260        "enddrag" : true,
35261        /**
35262         * @event dragdrop
35263         * Fires when a dragged node is dropped on a valid DD target
35264         * @param {Roo.tree.TreePanel} this
35265         * @param {Roo.tree.TreeNode} node
35266         * @param {DD} dd The dd it was dropped on
35267         * @param {event} e The raw browser event
35268         */
35269        "dragdrop" : true,
35270        /**
35271         * @event beforenodedrop
35272         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35273         * passed to handlers has the following properties:<br />
35274         * <ul style="padding:5px;padding-left:16px;">
35275         * <li>tree - The TreePanel</li>
35276         * <li>target - The node being targeted for the drop</li>
35277         * <li>data - The drag data from the drag source</li>
35278         * <li>point - The point of the drop - append, above or below</li>
35279         * <li>source - The drag source</li>
35280         * <li>rawEvent - Raw mouse event</li>
35281         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35282         * to be inserted by setting them on this object.</li>
35283         * <li>cancel - Set this to true to cancel the drop.</li>
35284         * </ul>
35285         * @param {Object} dropEvent
35286         */
35287        "beforenodedrop" : true,
35288        /**
35289         * @event nodedrop
35290         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35291         * passed to handlers has the following properties:<br />
35292         * <ul style="padding:5px;padding-left:16px;">
35293         * <li>tree - The TreePanel</li>
35294         * <li>target - The node being targeted for the drop</li>
35295         * <li>data - The drag data from the drag source</li>
35296         * <li>point - The point of the drop - append, above or below</li>
35297         * <li>source - The drag source</li>
35298         * <li>rawEvent - Raw mouse event</li>
35299         * <li>dropNode - Dropped node(s).</li>
35300         * </ul>
35301         * @param {Object} dropEvent
35302         */
35303        "nodedrop" : true,
35304         /**
35305         * @event nodedragover
35306         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35307         * passed to handlers has the following properties:<br />
35308         * <ul style="padding:5px;padding-left:16px;">
35309         * <li>tree - The TreePanel</li>
35310         * <li>target - The node being targeted for the drop</li>
35311         * <li>data - The drag data from the drag source</li>
35312         * <li>point - The point of the drop - append, above or below</li>
35313         * <li>source - The drag source</li>
35314         * <li>rawEvent - Raw mouse event</li>
35315         * <li>dropNode - Drop node(s) provided by the source.</li>
35316         * <li>cancel - Set this to true to signal drop not allowed.</li>
35317         * </ul>
35318         * @param {Object} dragOverEvent
35319         */
35320        "nodedragover" : true,
35321        /**
35322         * @event appendnode
35323         * Fires when append node to the tree
35324         * @param {Roo.tree.TreePanel} this
35325         * @param {Roo.tree.TreeNode} node
35326         * @param {Number} index The index of the newly appended node
35327         */
35328        "appendnode" : true
35329         
35330     });
35331     if(this.singleExpand){
35332        this.on("beforeexpand", this.restrictExpand, this);
35333     }
35334     if (this.editor) {
35335         this.editor.tree = this;
35336         this.editor = Roo.factory(this.editor, Roo.tree);
35337     }
35338     
35339     if (this.selModel) {
35340         this.selModel = Roo.factory(this.selModel, Roo.tree);
35341     }
35342    
35343 };
35344 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35345     rootVisible : true,
35346     animate: Roo.enableFx,
35347     lines : true,
35348     enableDD : false,
35349     hlDrop : Roo.enableFx,
35350   
35351     renderer: false,
35352     
35353     rendererTip: false,
35354     // private
35355     restrictExpand : function(node){
35356         var p = node.parentNode;
35357         if(p){
35358             if(p.expandedChild && p.expandedChild.parentNode == p){
35359                 p.expandedChild.collapse();
35360             }
35361             p.expandedChild = node;
35362         }
35363     },
35364
35365     // private override
35366     setRootNode : function(node){
35367         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35368         if(!this.rootVisible){
35369             node.ui = new Roo.tree.RootTreeNodeUI(node);
35370         }
35371         return node;
35372     },
35373
35374     /**
35375      * Returns the container element for this TreePanel
35376      */
35377     getEl : function(){
35378         return this.el;
35379     },
35380
35381     /**
35382      * Returns the default TreeLoader for this TreePanel
35383      */
35384     getLoader : function(){
35385         return this.loader;
35386     },
35387
35388     /**
35389      * Expand all nodes
35390      */
35391     expandAll : function(){
35392         this.root.expand(true);
35393     },
35394
35395     /**
35396      * Collapse all nodes
35397      */
35398     collapseAll : function(){
35399         this.root.collapse(true);
35400     },
35401
35402     /**
35403      * Returns the selection model used by this TreePanel
35404      */
35405     getSelectionModel : function(){
35406         if(!this.selModel){
35407             this.selModel = new Roo.tree.DefaultSelectionModel();
35408         }
35409         return this.selModel;
35410     },
35411
35412     /**
35413      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35414      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35415      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35416      * @return {Array}
35417      */
35418     getChecked : function(a, startNode){
35419         startNode = startNode || this.root;
35420         var r = [];
35421         var f = function(){
35422             if(this.attributes.checked){
35423                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35424             }
35425         }
35426         startNode.cascade(f);
35427         return r;
35428     },
35429
35430     /**
35431      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35432      * @param {String} path
35433      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35434      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35435      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35436      */
35437     expandPath : function(path, attr, callback){
35438         attr = attr || "id";
35439         var keys = path.split(this.pathSeparator);
35440         var curNode = this.root;
35441         if(curNode.attributes[attr] != keys[1]){ // invalid root
35442             if(callback){
35443                 callback(false, null);
35444             }
35445             return;
35446         }
35447         var index = 1;
35448         var f = function(){
35449             if(++index == keys.length){
35450                 if(callback){
35451                     callback(true, curNode);
35452                 }
35453                 return;
35454             }
35455             var c = curNode.findChild(attr, keys[index]);
35456             if(!c){
35457                 if(callback){
35458                     callback(false, curNode);
35459                 }
35460                 return;
35461             }
35462             curNode = c;
35463             c.expand(false, false, f);
35464         };
35465         curNode.expand(false, false, f);
35466     },
35467
35468     /**
35469      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35470      * @param {String} path
35471      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35472      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35473      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35474      */
35475     selectPath : function(path, attr, callback){
35476         attr = attr || "id";
35477         var keys = path.split(this.pathSeparator);
35478         var v = keys.pop();
35479         if(keys.length > 0){
35480             var f = function(success, node){
35481                 if(success && node){
35482                     var n = node.findChild(attr, v);
35483                     if(n){
35484                         n.select();
35485                         if(callback){
35486                             callback(true, n);
35487                         }
35488                     }else if(callback){
35489                         callback(false, n);
35490                     }
35491                 }else{
35492                     if(callback){
35493                         callback(false, n);
35494                     }
35495                 }
35496             };
35497             this.expandPath(keys.join(this.pathSeparator), attr, f);
35498         }else{
35499             this.root.select();
35500             if(callback){
35501                 callback(true, this.root);
35502             }
35503         }
35504     },
35505
35506     getTreeEl : function(){
35507         return this.el;
35508     },
35509
35510     /**
35511      * Trigger rendering of this TreePanel
35512      */
35513     render : function(){
35514         if (this.innerCt) {
35515             return this; // stop it rendering more than once!!
35516         }
35517         
35518         this.innerCt = this.el.createChild({tag:"ul",
35519                cls:"x-tree-root-ct " +
35520                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35521
35522         if(this.containerScroll){
35523             Roo.dd.ScrollManager.register(this.el);
35524         }
35525         if((this.enableDD || this.enableDrop) && !this.dropZone){
35526            /**
35527             * The dropZone used by this tree if drop is enabled
35528             * @type Roo.tree.TreeDropZone
35529             */
35530              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35531                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35532            });
35533         }
35534         if((this.enableDD || this.enableDrag) && !this.dragZone){
35535            /**
35536             * The dragZone used by this tree if drag is enabled
35537             * @type Roo.tree.TreeDragZone
35538             */
35539             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35540                ddGroup: this.ddGroup || "TreeDD",
35541                scroll: this.ddScroll
35542            });
35543         }
35544         this.getSelectionModel().init(this);
35545         if (!this.root) {
35546             Roo.log("ROOT not set in tree");
35547             return this;
35548         }
35549         this.root.render();
35550         if(!this.rootVisible){
35551             this.root.renderChildren();
35552         }
35553         return this;
35554     }
35555 });/*
35556  * Based on:
35557  * Ext JS Library 1.1.1
35558  * Copyright(c) 2006-2007, Ext JS, LLC.
35559  *
35560  * Originally Released Under LGPL - original licence link has changed is not relivant.
35561  *
35562  * Fork - LGPL
35563  * <script type="text/javascript">
35564  */
35565  
35566
35567 /**
35568  * @class Roo.tree.DefaultSelectionModel
35569  * @extends Roo.util.Observable
35570  * The default single selection for a TreePanel.
35571  * @param {Object} cfg Configuration
35572  */
35573 Roo.tree.DefaultSelectionModel = function(cfg){
35574    this.selNode = null;
35575    
35576    
35577    
35578    this.addEvents({
35579        /**
35580         * @event selectionchange
35581         * Fires when the selected node changes
35582         * @param {DefaultSelectionModel} this
35583         * @param {TreeNode} node the new selection
35584         */
35585        "selectionchange" : true,
35586
35587        /**
35588         * @event beforeselect
35589         * Fires before the selected node changes, return false to cancel the change
35590         * @param {DefaultSelectionModel} this
35591         * @param {TreeNode} node the new selection
35592         * @param {TreeNode} node the old selection
35593         */
35594        "beforeselect" : true
35595    });
35596    
35597     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35598 };
35599
35600 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35601     init : function(tree){
35602         this.tree = tree;
35603         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35604         tree.on("click", this.onNodeClick, this);
35605     },
35606     
35607     onNodeClick : function(node, e){
35608         if (e.ctrlKey && this.selNode == node)  {
35609             this.unselect(node);
35610             return;
35611         }
35612         this.select(node);
35613     },
35614     
35615     /**
35616      * Select a node.
35617      * @param {TreeNode} node The node to select
35618      * @return {TreeNode} The selected node
35619      */
35620     select : function(node){
35621         var last = this.selNode;
35622         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35623             if(last){
35624                 last.ui.onSelectedChange(false);
35625             }
35626             this.selNode = node;
35627             node.ui.onSelectedChange(true);
35628             this.fireEvent("selectionchange", this, node, last);
35629         }
35630         return node;
35631     },
35632     
35633     /**
35634      * Deselect a node.
35635      * @param {TreeNode} node The node to unselect
35636      */
35637     unselect : function(node){
35638         if(this.selNode == node){
35639             this.clearSelections();
35640         }    
35641     },
35642     
35643     /**
35644      * Clear all selections
35645      */
35646     clearSelections : function(){
35647         var n = this.selNode;
35648         if(n){
35649             n.ui.onSelectedChange(false);
35650             this.selNode = null;
35651             this.fireEvent("selectionchange", this, null);
35652         }
35653         return n;
35654     },
35655     
35656     /**
35657      * Get the selected node
35658      * @return {TreeNode} The selected node
35659      */
35660     getSelectedNode : function(){
35661         return this.selNode;    
35662     },
35663     
35664     /**
35665      * Returns true if the node is selected
35666      * @param {TreeNode} node The node to check
35667      * @return {Boolean}
35668      */
35669     isSelected : function(node){
35670         return this.selNode == node;  
35671     },
35672
35673     /**
35674      * Selects the node above the selected node in the tree, intelligently walking the nodes
35675      * @return TreeNode The new selection
35676      */
35677     selectPrevious : function(){
35678         var s = this.selNode || this.lastSelNode;
35679         if(!s){
35680             return null;
35681         }
35682         var ps = s.previousSibling;
35683         if(ps){
35684             if(!ps.isExpanded() || ps.childNodes.length < 1){
35685                 return this.select(ps);
35686             } else{
35687                 var lc = ps.lastChild;
35688                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35689                     lc = lc.lastChild;
35690                 }
35691                 return this.select(lc);
35692             }
35693         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35694             return this.select(s.parentNode);
35695         }
35696         return null;
35697     },
35698
35699     /**
35700      * Selects the node above the selected node in the tree, intelligently walking the nodes
35701      * @return TreeNode The new selection
35702      */
35703     selectNext : function(){
35704         var s = this.selNode || this.lastSelNode;
35705         if(!s){
35706             return null;
35707         }
35708         if(s.firstChild && s.isExpanded()){
35709              return this.select(s.firstChild);
35710          }else if(s.nextSibling){
35711              return this.select(s.nextSibling);
35712          }else if(s.parentNode){
35713             var newS = null;
35714             s.parentNode.bubble(function(){
35715                 if(this.nextSibling){
35716                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35717                     return false;
35718                 }
35719             });
35720             return newS;
35721          }
35722         return null;
35723     },
35724
35725     onKeyDown : function(e){
35726         var s = this.selNode || this.lastSelNode;
35727         // undesirable, but required
35728         var sm = this;
35729         if(!s){
35730             return;
35731         }
35732         var k = e.getKey();
35733         switch(k){
35734              case e.DOWN:
35735                  e.stopEvent();
35736                  this.selectNext();
35737              break;
35738              case e.UP:
35739                  e.stopEvent();
35740                  this.selectPrevious();
35741              break;
35742              case e.RIGHT:
35743                  e.preventDefault();
35744                  if(s.hasChildNodes()){
35745                      if(!s.isExpanded()){
35746                          s.expand();
35747                      }else if(s.firstChild){
35748                          this.select(s.firstChild, e);
35749                      }
35750                  }
35751              break;
35752              case e.LEFT:
35753                  e.preventDefault();
35754                  if(s.hasChildNodes() && s.isExpanded()){
35755                      s.collapse();
35756                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35757                      this.select(s.parentNode, e);
35758                  }
35759              break;
35760         };
35761     }
35762 });
35763
35764 /**
35765  * @class Roo.tree.MultiSelectionModel
35766  * @extends Roo.util.Observable
35767  * Multi selection for a TreePanel.
35768  * @param {Object} cfg Configuration
35769  */
35770 Roo.tree.MultiSelectionModel = function(){
35771    this.selNodes = [];
35772    this.selMap = {};
35773    this.addEvents({
35774        /**
35775         * @event selectionchange
35776         * Fires when the selected nodes change
35777         * @param {MultiSelectionModel} this
35778         * @param {Array} nodes Array of the selected nodes
35779         */
35780        "selectionchange" : true
35781    });
35782    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35783    
35784 };
35785
35786 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35787     init : function(tree){
35788         this.tree = tree;
35789         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35790         tree.on("click", this.onNodeClick, this);
35791     },
35792     
35793     onNodeClick : function(node, e){
35794         this.select(node, e, e.ctrlKey);
35795     },
35796     
35797     /**
35798      * Select a node.
35799      * @param {TreeNode} node The node to select
35800      * @param {EventObject} e (optional) An event associated with the selection
35801      * @param {Boolean} keepExisting True to retain existing selections
35802      * @return {TreeNode} The selected node
35803      */
35804     select : function(node, e, keepExisting){
35805         if(keepExisting !== true){
35806             this.clearSelections(true);
35807         }
35808         if(this.isSelected(node)){
35809             this.lastSelNode = node;
35810             return node;
35811         }
35812         this.selNodes.push(node);
35813         this.selMap[node.id] = node;
35814         this.lastSelNode = node;
35815         node.ui.onSelectedChange(true);
35816         this.fireEvent("selectionchange", this, this.selNodes);
35817         return node;
35818     },
35819     
35820     /**
35821      * Deselect a node.
35822      * @param {TreeNode} node The node to unselect
35823      */
35824     unselect : function(node){
35825         if(this.selMap[node.id]){
35826             node.ui.onSelectedChange(false);
35827             var sn = this.selNodes;
35828             var index = -1;
35829             if(sn.indexOf){
35830                 index = sn.indexOf(node);
35831             }else{
35832                 for(var i = 0, len = sn.length; i < len; i++){
35833                     if(sn[i] == node){
35834                         index = i;
35835                         break;
35836                     }
35837                 }
35838             }
35839             if(index != -1){
35840                 this.selNodes.splice(index, 1);
35841             }
35842             delete this.selMap[node.id];
35843             this.fireEvent("selectionchange", this, this.selNodes);
35844         }
35845     },
35846     
35847     /**
35848      * Clear all selections
35849      */
35850     clearSelections : function(suppressEvent){
35851         var sn = this.selNodes;
35852         if(sn.length > 0){
35853             for(var i = 0, len = sn.length; i < len; i++){
35854                 sn[i].ui.onSelectedChange(false);
35855             }
35856             this.selNodes = [];
35857             this.selMap = {};
35858             if(suppressEvent !== true){
35859                 this.fireEvent("selectionchange", this, this.selNodes);
35860             }
35861         }
35862     },
35863     
35864     /**
35865      * Returns true if the node is selected
35866      * @param {TreeNode} node The node to check
35867      * @return {Boolean}
35868      */
35869     isSelected : function(node){
35870         return this.selMap[node.id] ? true : false;  
35871     },
35872     
35873     /**
35874      * Returns an array of the selected nodes
35875      * @return {Array}
35876      */
35877     getSelectedNodes : function(){
35878         return this.selNodes;    
35879     },
35880
35881     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35882
35883     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35884
35885     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35886 });/*
35887  * Based on:
35888  * Ext JS Library 1.1.1
35889  * Copyright(c) 2006-2007, Ext JS, LLC.
35890  *
35891  * Originally Released Under LGPL - original licence link has changed is not relivant.
35892  *
35893  * Fork - LGPL
35894  * <script type="text/javascript">
35895  */
35896  
35897 /**
35898  * @class Roo.tree.TreeNode
35899  * @extends Roo.data.Node
35900  * @cfg {String} text The text for this node
35901  * @cfg {Boolean} expanded true to start the node expanded
35902  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35903  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35904  * @cfg {Boolean} disabled true to start the node disabled
35905  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35906  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35907  * @cfg {String} cls A css class to be added to the node
35908  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35909  * @cfg {String} href URL of the link used for the node (defaults to #)
35910  * @cfg {String} hrefTarget target frame for the link
35911  * @cfg {String} qtip An Ext QuickTip for the node
35912  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35913  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35914  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35915  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35916  * (defaults to undefined with no checkbox rendered)
35917  * @constructor
35918  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35919  */
35920 Roo.tree.TreeNode = function(attributes){
35921     attributes = attributes || {};
35922     if(typeof attributes == "string"){
35923         attributes = {text: attributes};
35924     }
35925     this.childrenRendered = false;
35926     this.rendered = false;
35927     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35928     this.expanded = attributes.expanded === true;
35929     this.isTarget = attributes.isTarget !== false;
35930     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35931     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35932
35933     /**
35934      * Read-only. The text for this node. To change it use setText().
35935      * @type String
35936      */
35937     this.text = attributes.text;
35938     /**
35939      * True if this node is disabled.
35940      * @type Boolean
35941      */
35942     this.disabled = attributes.disabled === true;
35943
35944     this.addEvents({
35945         /**
35946         * @event textchange
35947         * Fires when the text for this node is changed
35948         * @param {Node} this This node
35949         * @param {String} text The new text
35950         * @param {String} oldText The old text
35951         */
35952         "textchange" : true,
35953         /**
35954         * @event beforeexpand
35955         * Fires before this node is expanded, return false to cancel.
35956         * @param {Node} this This node
35957         * @param {Boolean} deep
35958         * @param {Boolean} anim
35959         */
35960         "beforeexpand" : true,
35961         /**
35962         * @event beforecollapse
35963         * Fires before this node is collapsed, return false to cancel.
35964         * @param {Node} this This node
35965         * @param {Boolean} deep
35966         * @param {Boolean} anim
35967         */
35968         "beforecollapse" : true,
35969         /**
35970         * @event expand
35971         * Fires when this node is expanded
35972         * @param {Node} this This node
35973         */
35974         "expand" : true,
35975         /**
35976         * @event disabledchange
35977         * Fires when the disabled status of this node changes
35978         * @param {Node} this This node
35979         * @param {Boolean} disabled
35980         */
35981         "disabledchange" : true,
35982         /**
35983         * @event collapse
35984         * Fires when this node is collapsed
35985         * @param {Node} this This node
35986         */
35987         "collapse" : true,
35988         /**
35989         * @event beforeclick
35990         * Fires before click processing. Return false to cancel the default action.
35991         * @param {Node} this This node
35992         * @param {Roo.EventObject} e The event object
35993         */
35994         "beforeclick":true,
35995         /**
35996         * @event checkchange
35997         * Fires when a node with a checkbox's checked property changes
35998         * @param {Node} this This node
35999         * @param {Boolean} checked
36000         */
36001         "checkchange":true,
36002         /**
36003         * @event click
36004         * Fires when this node is clicked
36005         * @param {Node} this This node
36006         * @param {Roo.EventObject} e The event object
36007         */
36008         "click":true,
36009         /**
36010         * @event dblclick
36011         * Fires when this node is double clicked
36012         * @param {Node} this This node
36013         * @param {Roo.EventObject} e The event object
36014         */
36015         "dblclick":true,
36016         /**
36017         * @event contextmenu
36018         * Fires when this node is right clicked
36019         * @param {Node} this This node
36020         * @param {Roo.EventObject} e The event object
36021         */
36022         "contextmenu":true,
36023         /**
36024         * @event beforechildrenrendered
36025         * Fires right before the child nodes for this node are rendered
36026         * @param {Node} this This node
36027         */
36028         "beforechildrenrendered":true
36029     });
36030
36031     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36032
36033     /**
36034      * Read-only. The UI for this node
36035      * @type TreeNodeUI
36036      */
36037     this.ui = new uiClass(this);
36038     
36039     // finally support items[]
36040     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36041         return;
36042     }
36043     
36044     
36045     Roo.each(this.attributes.items, function(c) {
36046         this.appendChild(Roo.factory(c,Roo.Tree));
36047     }, this);
36048     delete this.attributes.items;
36049     
36050     
36051     
36052 };
36053 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36054     preventHScroll: true,
36055     /**
36056      * Returns true if this node is expanded
36057      * @return {Boolean}
36058      */
36059     isExpanded : function(){
36060         return this.expanded;
36061     },
36062
36063     /**
36064      * Returns the UI object for this node
36065      * @return {TreeNodeUI}
36066      */
36067     getUI : function(){
36068         return this.ui;
36069     },
36070
36071     // private override
36072     setFirstChild : function(node){
36073         var of = this.firstChild;
36074         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36075         if(this.childrenRendered && of && node != of){
36076             of.renderIndent(true, true);
36077         }
36078         if(this.rendered){
36079             this.renderIndent(true, true);
36080         }
36081     },
36082
36083     // private override
36084     setLastChild : function(node){
36085         var ol = this.lastChild;
36086         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36087         if(this.childrenRendered && ol && node != ol){
36088             ol.renderIndent(true, true);
36089         }
36090         if(this.rendered){
36091             this.renderIndent(true, true);
36092         }
36093     },
36094
36095     // these methods are overridden to provide lazy rendering support
36096     // private override
36097     appendChild : function()
36098     {
36099         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36100         if(node && this.childrenRendered){
36101             node.render();
36102         }
36103         this.ui.updateExpandIcon();
36104         return node;
36105     },
36106
36107     // private override
36108     removeChild : function(node){
36109         this.ownerTree.getSelectionModel().unselect(node);
36110         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36111         // if it's been rendered remove dom node
36112         if(this.childrenRendered){
36113             node.ui.remove();
36114         }
36115         if(this.childNodes.length < 1){
36116             this.collapse(false, false);
36117         }else{
36118             this.ui.updateExpandIcon();
36119         }
36120         if(!this.firstChild) {
36121             this.childrenRendered = false;
36122         }
36123         return node;
36124     },
36125
36126     // private override
36127     insertBefore : function(node, refNode){
36128         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36129         if(newNode && refNode && this.childrenRendered){
36130             node.render();
36131         }
36132         this.ui.updateExpandIcon();
36133         return newNode;
36134     },
36135
36136     /**
36137      * Sets the text for this node
36138      * @param {String} text
36139      */
36140     setText : function(text){
36141         var oldText = this.text;
36142         this.text = text;
36143         this.attributes.text = text;
36144         if(this.rendered){ // event without subscribing
36145             this.ui.onTextChange(this, text, oldText);
36146         }
36147         this.fireEvent("textchange", this, text, oldText);
36148     },
36149
36150     /**
36151      * Triggers selection of this node
36152      */
36153     select : function(){
36154         this.getOwnerTree().getSelectionModel().select(this);
36155     },
36156
36157     /**
36158      * Triggers deselection of this node
36159      */
36160     unselect : function(){
36161         this.getOwnerTree().getSelectionModel().unselect(this);
36162     },
36163
36164     /**
36165      * Returns true if this node is selected
36166      * @return {Boolean}
36167      */
36168     isSelected : function(){
36169         return this.getOwnerTree().getSelectionModel().isSelected(this);
36170     },
36171
36172     /**
36173      * Expand this node.
36174      * @param {Boolean} deep (optional) True to expand all children as well
36175      * @param {Boolean} anim (optional) false to cancel the default animation
36176      * @param {Function} callback (optional) A callback to be called when
36177      * expanding this node completes (does not wait for deep expand to complete).
36178      * Called with 1 parameter, this node.
36179      */
36180     expand : function(deep, anim, callback){
36181         if(!this.expanded){
36182             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36183                 return;
36184             }
36185             if(!this.childrenRendered){
36186                 this.renderChildren();
36187             }
36188             this.expanded = true;
36189             
36190             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36191                 this.ui.animExpand(function(){
36192                     this.fireEvent("expand", this);
36193                     if(typeof callback == "function"){
36194                         callback(this);
36195                     }
36196                     if(deep === true){
36197                         this.expandChildNodes(true);
36198                     }
36199                 }.createDelegate(this));
36200                 return;
36201             }else{
36202                 this.ui.expand();
36203                 this.fireEvent("expand", this);
36204                 if(typeof callback == "function"){
36205                     callback(this);
36206                 }
36207             }
36208         }else{
36209            if(typeof callback == "function"){
36210                callback(this);
36211            }
36212         }
36213         if(deep === true){
36214             this.expandChildNodes(true);
36215         }
36216     },
36217
36218     isHiddenRoot : function(){
36219         return this.isRoot && !this.getOwnerTree().rootVisible;
36220     },
36221
36222     /**
36223      * Collapse this node.
36224      * @param {Boolean} deep (optional) True to collapse all children as well
36225      * @param {Boolean} anim (optional) false to cancel the default animation
36226      */
36227     collapse : function(deep, anim){
36228         if(this.expanded && !this.isHiddenRoot()){
36229             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36230                 return;
36231             }
36232             this.expanded = false;
36233             if((this.getOwnerTree().animate && anim !== false) || anim){
36234                 this.ui.animCollapse(function(){
36235                     this.fireEvent("collapse", this);
36236                     if(deep === true){
36237                         this.collapseChildNodes(true);
36238                     }
36239                 }.createDelegate(this));
36240                 return;
36241             }else{
36242                 this.ui.collapse();
36243                 this.fireEvent("collapse", this);
36244             }
36245         }
36246         if(deep === true){
36247             var cs = this.childNodes;
36248             for(var i = 0, len = cs.length; i < len; i++) {
36249                 cs[i].collapse(true, false);
36250             }
36251         }
36252     },
36253
36254     // private
36255     delayedExpand : function(delay){
36256         if(!this.expandProcId){
36257             this.expandProcId = this.expand.defer(delay, this);
36258         }
36259     },
36260
36261     // private
36262     cancelExpand : function(){
36263         if(this.expandProcId){
36264             clearTimeout(this.expandProcId);
36265         }
36266         this.expandProcId = false;
36267     },
36268
36269     /**
36270      * Toggles expanded/collapsed state of the node
36271      */
36272     toggle : function(){
36273         if(this.expanded){
36274             this.collapse();
36275         }else{
36276             this.expand();
36277         }
36278     },
36279
36280     /**
36281      * Ensures all parent nodes are expanded
36282      */
36283     ensureVisible : function(callback){
36284         var tree = this.getOwnerTree();
36285         tree.expandPath(this.parentNode.getPath(), false, function(){
36286             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36287             Roo.callback(callback);
36288         }.createDelegate(this));
36289     },
36290
36291     /**
36292      * Expand all child nodes
36293      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36294      */
36295     expandChildNodes : function(deep){
36296         var cs = this.childNodes;
36297         for(var i = 0, len = cs.length; i < len; i++) {
36298                 cs[i].expand(deep);
36299         }
36300     },
36301
36302     /**
36303      * Collapse all child nodes
36304      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36305      */
36306     collapseChildNodes : function(deep){
36307         var cs = this.childNodes;
36308         for(var i = 0, len = cs.length; i < len; i++) {
36309                 cs[i].collapse(deep);
36310         }
36311     },
36312
36313     /**
36314      * Disables this node
36315      */
36316     disable : function(){
36317         this.disabled = true;
36318         this.unselect();
36319         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36320             this.ui.onDisableChange(this, true);
36321         }
36322         this.fireEvent("disabledchange", this, true);
36323     },
36324
36325     /**
36326      * Enables this node
36327      */
36328     enable : function(){
36329         this.disabled = false;
36330         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36331             this.ui.onDisableChange(this, false);
36332         }
36333         this.fireEvent("disabledchange", this, false);
36334     },
36335
36336     // private
36337     renderChildren : function(suppressEvent){
36338         if(suppressEvent !== false){
36339             this.fireEvent("beforechildrenrendered", this);
36340         }
36341         var cs = this.childNodes;
36342         for(var i = 0, len = cs.length; i < len; i++){
36343             cs[i].render(true);
36344         }
36345         this.childrenRendered = true;
36346     },
36347
36348     // private
36349     sort : function(fn, scope){
36350         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36351         if(this.childrenRendered){
36352             var cs = this.childNodes;
36353             for(var i = 0, len = cs.length; i < len; i++){
36354                 cs[i].render(true);
36355             }
36356         }
36357     },
36358
36359     // private
36360     render : function(bulkRender){
36361         this.ui.render(bulkRender);
36362         if(!this.rendered){
36363             this.rendered = true;
36364             if(this.expanded){
36365                 this.expanded = false;
36366                 this.expand(false, false);
36367             }
36368         }
36369     },
36370
36371     // private
36372     renderIndent : function(deep, refresh){
36373         if(refresh){
36374             this.ui.childIndent = null;
36375         }
36376         this.ui.renderIndent();
36377         if(deep === true && this.childrenRendered){
36378             var cs = this.childNodes;
36379             for(var i = 0, len = cs.length; i < len; i++){
36380                 cs[i].renderIndent(true, refresh);
36381             }
36382         }
36383     }
36384 });/*
36385  * Based on:
36386  * Ext JS Library 1.1.1
36387  * Copyright(c) 2006-2007, Ext JS, LLC.
36388  *
36389  * Originally Released Under LGPL - original licence link has changed is not relivant.
36390  *
36391  * Fork - LGPL
36392  * <script type="text/javascript">
36393  */
36394  
36395 /**
36396  * @class Roo.tree.AsyncTreeNode
36397  * @extends Roo.tree.TreeNode
36398  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36399  * @constructor
36400  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36401  */
36402  Roo.tree.AsyncTreeNode = function(config){
36403     this.loaded = false;
36404     this.loading = false;
36405     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36406     /**
36407     * @event beforeload
36408     * Fires before this node is loaded, return false to cancel
36409     * @param {Node} this This node
36410     */
36411     this.addEvents({'beforeload':true, 'load': true});
36412     /**
36413     * @event load
36414     * Fires when this node is loaded
36415     * @param {Node} this This node
36416     */
36417     /**
36418      * The loader used by this node (defaults to using the tree's defined loader)
36419      * @type TreeLoader
36420      * @property loader
36421      */
36422 };
36423 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36424     expand : function(deep, anim, callback){
36425         if(this.loading){ // if an async load is already running, waiting til it's done
36426             var timer;
36427             var f = function(){
36428                 if(!this.loading){ // done loading
36429                     clearInterval(timer);
36430                     this.expand(deep, anim, callback);
36431                 }
36432             }.createDelegate(this);
36433             timer = setInterval(f, 200);
36434             return;
36435         }
36436         if(!this.loaded){
36437             if(this.fireEvent("beforeload", this) === false){
36438                 return;
36439             }
36440             this.loading = true;
36441             this.ui.beforeLoad(this);
36442             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36443             if(loader){
36444                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36445                 return;
36446             }
36447         }
36448         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36449     },
36450     
36451     /**
36452      * Returns true if this node is currently loading
36453      * @return {Boolean}
36454      */
36455     isLoading : function(){
36456         return this.loading;  
36457     },
36458     
36459     loadComplete : function(deep, anim, callback){
36460         this.loading = false;
36461         this.loaded = true;
36462         this.ui.afterLoad(this);
36463         this.fireEvent("load", this);
36464         this.expand(deep, anim, callback);
36465     },
36466     
36467     /**
36468      * Returns true if this node has been loaded
36469      * @return {Boolean}
36470      */
36471     isLoaded : function(){
36472         return this.loaded;
36473     },
36474     
36475     hasChildNodes : function(){
36476         if(!this.isLeaf() && !this.loaded){
36477             return true;
36478         }else{
36479             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36480         }
36481     },
36482
36483     /**
36484      * Trigger a reload for this node
36485      * @param {Function} callback
36486      */
36487     reload : function(callback){
36488         this.collapse(false, false);
36489         while(this.firstChild){
36490             this.removeChild(this.firstChild);
36491         }
36492         this.childrenRendered = false;
36493         this.loaded = false;
36494         if(this.isHiddenRoot()){
36495             this.expanded = false;
36496         }
36497         this.expand(false, false, callback);
36498     }
36499 });/*
36500  * Based on:
36501  * Ext JS Library 1.1.1
36502  * Copyright(c) 2006-2007, Ext JS, LLC.
36503  *
36504  * Originally Released Under LGPL - original licence link has changed is not relivant.
36505  *
36506  * Fork - LGPL
36507  * <script type="text/javascript">
36508  */
36509  
36510 /**
36511  * @class Roo.tree.TreeNodeUI
36512  * @constructor
36513  * @param {Object} node The node to render
36514  * The TreeNode UI implementation is separate from the
36515  * tree implementation. Unless you are customizing the tree UI,
36516  * you should never have to use this directly.
36517  */
36518 Roo.tree.TreeNodeUI = function(node){
36519     this.node = node;
36520     this.rendered = false;
36521     this.animating = false;
36522     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36523 };
36524
36525 Roo.tree.TreeNodeUI.prototype = {
36526     removeChild : function(node){
36527         if(this.rendered){
36528             this.ctNode.removeChild(node.ui.getEl());
36529         }
36530     },
36531
36532     beforeLoad : function(){
36533          this.addClass("x-tree-node-loading");
36534     },
36535
36536     afterLoad : function(){
36537          this.removeClass("x-tree-node-loading");
36538     },
36539
36540     onTextChange : function(node, text, oldText){
36541         if(this.rendered){
36542             this.textNode.innerHTML = text;
36543         }
36544     },
36545
36546     onDisableChange : function(node, state){
36547         this.disabled = state;
36548         if(state){
36549             this.addClass("x-tree-node-disabled");
36550         }else{
36551             this.removeClass("x-tree-node-disabled");
36552         }
36553     },
36554
36555     onSelectedChange : function(state){
36556         if(state){
36557             this.focus();
36558             this.addClass("x-tree-selected");
36559         }else{
36560             //this.blur();
36561             this.removeClass("x-tree-selected");
36562         }
36563     },
36564
36565     onMove : function(tree, node, oldParent, newParent, index, refNode){
36566         this.childIndent = null;
36567         if(this.rendered){
36568             var targetNode = newParent.ui.getContainer();
36569             if(!targetNode){//target not rendered
36570                 this.holder = document.createElement("div");
36571                 this.holder.appendChild(this.wrap);
36572                 return;
36573             }
36574             var insertBefore = refNode ? refNode.ui.getEl() : null;
36575             if(insertBefore){
36576                 targetNode.insertBefore(this.wrap, insertBefore);
36577             }else{
36578                 targetNode.appendChild(this.wrap);
36579             }
36580             this.node.renderIndent(true);
36581         }
36582     },
36583
36584     addClass : function(cls){
36585         if(this.elNode){
36586             Roo.fly(this.elNode).addClass(cls);
36587         }
36588     },
36589
36590     removeClass : function(cls){
36591         if(this.elNode){
36592             Roo.fly(this.elNode).removeClass(cls);
36593         }
36594     },
36595
36596     remove : function(){
36597         if(this.rendered){
36598             this.holder = document.createElement("div");
36599             this.holder.appendChild(this.wrap);
36600         }
36601     },
36602
36603     fireEvent : function(){
36604         return this.node.fireEvent.apply(this.node, arguments);
36605     },
36606
36607     initEvents : function(){
36608         this.node.on("move", this.onMove, this);
36609         var E = Roo.EventManager;
36610         var a = this.anchor;
36611
36612         var el = Roo.fly(a, '_treeui');
36613
36614         if(Roo.isOpera){ // opera render bug ignores the CSS
36615             el.setStyle("text-decoration", "none");
36616         }
36617
36618         el.on("click", this.onClick, this);
36619         el.on("dblclick", this.onDblClick, this);
36620
36621         if(this.checkbox){
36622             Roo.EventManager.on(this.checkbox,
36623                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36624         }
36625
36626         el.on("contextmenu", this.onContextMenu, this);
36627
36628         var icon = Roo.fly(this.iconNode);
36629         icon.on("click", this.onClick, this);
36630         icon.on("dblclick", this.onDblClick, this);
36631         icon.on("contextmenu", this.onContextMenu, this);
36632         E.on(this.ecNode, "click", this.ecClick, this, true);
36633
36634         if(this.node.disabled){
36635             this.addClass("x-tree-node-disabled");
36636         }
36637         if(this.node.hidden){
36638             this.addClass("x-tree-node-disabled");
36639         }
36640         var ot = this.node.getOwnerTree();
36641         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36642         if(dd && (!this.node.isRoot || ot.rootVisible)){
36643             Roo.dd.Registry.register(this.elNode, {
36644                 node: this.node,
36645                 handles: this.getDDHandles(),
36646                 isHandle: false
36647             });
36648         }
36649     },
36650
36651     getDDHandles : function(){
36652         return [this.iconNode, this.textNode];
36653     },
36654
36655     hide : function(){
36656         if(this.rendered){
36657             this.wrap.style.display = "none";
36658         }
36659     },
36660
36661     show : function(){
36662         if(this.rendered){
36663             this.wrap.style.display = "";
36664         }
36665     },
36666
36667     onContextMenu : function(e){
36668         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36669             e.preventDefault();
36670             this.focus();
36671             this.fireEvent("contextmenu", this.node, e);
36672         }
36673     },
36674
36675     onClick : function(e){
36676         if(this.dropping){
36677             e.stopEvent();
36678             return;
36679         }
36680         if(this.fireEvent("beforeclick", this.node, e) !== false){
36681             if(!this.disabled && this.node.attributes.href){
36682                 this.fireEvent("click", this.node, e);
36683                 return;
36684             }
36685             e.preventDefault();
36686             if(this.disabled){
36687                 return;
36688             }
36689
36690             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36691                 this.node.toggle();
36692             }
36693
36694             this.fireEvent("click", this.node, e);
36695         }else{
36696             e.stopEvent();
36697         }
36698     },
36699
36700     onDblClick : function(e){
36701         e.preventDefault();
36702         if(this.disabled){
36703             return;
36704         }
36705         if(this.checkbox){
36706             this.toggleCheck();
36707         }
36708         if(!this.animating && this.node.hasChildNodes()){
36709             this.node.toggle();
36710         }
36711         this.fireEvent("dblclick", this.node, e);
36712     },
36713
36714     onCheckChange : function(){
36715         var checked = this.checkbox.checked;
36716         this.node.attributes.checked = checked;
36717         this.fireEvent('checkchange', this.node, checked);
36718     },
36719
36720     ecClick : function(e){
36721         if(!this.animating && this.node.hasChildNodes()){
36722             this.node.toggle();
36723         }
36724     },
36725
36726     startDrop : function(){
36727         this.dropping = true;
36728     },
36729
36730     // delayed drop so the click event doesn't get fired on a drop
36731     endDrop : function(){
36732        setTimeout(function(){
36733            this.dropping = false;
36734        }.createDelegate(this), 50);
36735     },
36736
36737     expand : function(){
36738         this.updateExpandIcon();
36739         this.ctNode.style.display = "";
36740     },
36741
36742     focus : function(){
36743         if(!this.node.preventHScroll){
36744             try{this.anchor.focus();
36745             }catch(e){}
36746         }else if(!Roo.isIE){
36747             try{
36748                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36749                 var l = noscroll.scrollLeft;
36750                 this.anchor.focus();
36751                 noscroll.scrollLeft = l;
36752             }catch(e){}
36753         }
36754     },
36755
36756     toggleCheck : function(value){
36757         var cb = this.checkbox;
36758         if(cb){
36759             cb.checked = (value === undefined ? !cb.checked : value);
36760         }
36761     },
36762
36763     blur : function(){
36764         try{
36765             this.anchor.blur();
36766         }catch(e){}
36767     },
36768
36769     animExpand : function(callback){
36770         var ct = Roo.get(this.ctNode);
36771         ct.stopFx();
36772         if(!this.node.hasChildNodes()){
36773             this.updateExpandIcon();
36774             this.ctNode.style.display = "";
36775             Roo.callback(callback);
36776             return;
36777         }
36778         this.animating = true;
36779         this.updateExpandIcon();
36780
36781         ct.slideIn('t', {
36782            callback : function(){
36783                this.animating = false;
36784                Roo.callback(callback);
36785             },
36786             scope: this,
36787             duration: this.node.ownerTree.duration || .25
36788         });
36789     },
36790
36791     highlight : function(){
36792         var tree = this.node.getOwnerTree();
36793         Roo.fly(this.wrap).highlight(
36794             tree.hlColor || "C3DAF9",
36795             {endColor: tree.hlBaseColor}
36796         );
36797     },
36798
36799     collapse : function(){
36800         this.updateExpandIcon();
36801         this.ctNode.style.display = "none";
36802     },
36803
36804     animCollapse : function(callback){
36805         var ct = Roo.get(this.ctNode);
36806         ct.enableDisplayMode('block');
36807         ct.stopFx();
36808
36809         this.animating = true;
36810         this.updateExpandIcon();
36811
36812         ct.slideOut('t', {
36813             callback : function(){
36814                this.animating = false;
36815                Roo.callback(callback);
36816             },
36817             scope: this,
36818             duration: this.node.ownerTree.duration || .25
36819         });
36820     },
36821
36822     getContainer : function(){
36823         return this.ctNode;
36824     },
36825
36826     getEl : function(){
36827         return this.wrap;
36828     },
36829
36830     appendDDGhost : function(ghostNode){
36831         ghostNode.appendChild(this.elNode.cloneNode(true));
36832     },
36833
36834     getDDRepairXY : function(){
36835         return Roo.lib.Dom.getXY(this.iconNode);
36836     },
36837
36838     onRender : function(){
36839         this.render();
36840     },
36841
36842     render : function(bulkRender){
36843         var n = this.node, a = n.attributes;
36844         var targetNode = n.parentNode ?
36845               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36846
36847         if(!this.rendered){
36848             this.rendered = true;
36849
36850             this.renderElements(n, a, targetNode, bulkRender);
36851
36852             if(a.qtip){
36853                if(this.textNode.setAttributeNS){
36854                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36855                    if(a.qtipTitle){
36856                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36857                    }
36858                }else{
36859                    this.textNode.setAttribute("ext:qtip", a.qtip);
36860                    if(a.qtipTitle){
36861                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36862                    }
36863                }
36864             }else if(a.qtipCfg){
36865                 a.qtipCfg.target = Roo.id(this.textNode);
36866                 Roo.QuickTips.register(a.qtipCfg);
36867             }
36868             this.initEvents();
36869             if(!this.node.expanded){
36870                 this.updateExpandIcon();
36871             }
36872         }else{
36873             if(bulkRender === true) {
36874                 targetNode.appendChild(this.wrap);
36875             }
36876         }
36877     },
36878
36879     renderElements : function(n, a, targetNode, bulkRender)
36880     {
36881         // add some indent caching, this helps performance when rendering a large tree
36882         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36883         var t = n.getOwnerTree();
36884         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36885         if (typeof(n.attributes.html) != 'undefined') {
36886             txt = n.attributes.html;
36887         }
36888         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36889         var cb = typeof a.checked == 'boolean';
36890         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36891         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36892             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36893             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36894             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36895             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36896             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36897              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36898                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36899             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36900             "</li>"];
36901
36902         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36903             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36904                                 n.nextSibling.ui.getEl(), buf.join(""));
36905         }else{
36906             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36907         }
36908
36909         this.elNode = this.wrap.childNodes[0];
36910         this.ctNode = this.wrap.childNodes[1];
36911         var cs = this.elNode.childNodes;
36912         this.indentNode = cs[0];
36913         this.ecNode = cs[1];
36914         this.iconNode = cs[2];
36915         var index = 3;
36916         if(cb){
36917             this.checkbox = cs[3];
36918             index++;
36919         }
36920         this.anchor = cs[index];
36921         this.textNode = cs[index].firstChild;
36922     },
36923
36924     getAnchor : function(){
36925         return this.anchor;
36926     },
36927
36928     getTextEl : function(){
36929         return this.textNode;
36930     },
36931
36932     getIconEl : function(){
36933         return this.iconNode;
36934     },
36935
36936     isChecked : function(){
36937         return this.checkbox ? this.checkbox.checked : false;
36938     },
36939
36940     updateExpandIcon : function(){
36941         if(this.rendered){
36942             var n = this.node, c1, c2;
36943             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36944             var hasChild = n.hasChildNodes();
36945             if(hasChild){
36946                 if(n.expanded){
36947                     cls += "-minus";
36948                     c1 = "x-tree-node-collapsed";
36949                     c2 = "x-tree-node-expanded";
36950                 }else{
36951                     cls += "-plus";
36952                     c1 = "x-tree-node-expanded";
36953                     c2 = "x-tree-node-collapsed";
36954                 }
36955                 if(this.wasLeaf){
36956                     this.removeClass("x-tree-node-leaf");
36957                     this.wasLeaf = false;
36958                 }
36959                 if(this.c1 != c1 || this.c2 != c2){
36960                     Roo.fly(this.elNode).replaceClass(c1, c2);
36961                     this.c1 = c1; this.c2 = c2;
36962                 }
36963             }else{
36964                 // this changes non-leafs into leafs if they have no children.
36965                 // it's not very rational behaviour..
36966                 
36967                 if(!this.wasLeaf && this.node.leaf){
36968                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36969                     delete this.c1;
36970                     delete this.c2;
36971                     this.wasLeaf = true;
36972                 }
36973             }
36974             var ecc = "x-tree-ec-icon "+cls;
36975             if(this.ecc != ecc){
36976                 this.ecNode.className = ecc;
36977                 this.ecc = ecc;
36978             }
36979         }
36980     },
36981
36982     getChildIndent : function(){
36983         if(!this.childIndent){
36984             var buf = [];
36985             var p = this.node;
36986             while(p){
36987                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36988                     if(!p.isLast()) {
36989                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36990                     } else {
36991                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36992                     }
36993                 }
36994                 p = p.parentNode;
36995             }
36996             this.childIndent = buf.join("");
36997         }
36998         return this.childIndent;
36999     },
37000
37001     renderIndent : function(){
37002         if(this.rendered){
37003             var indent = "";
37004             var p = this.node.parentNode;
37005             if(p){
37006                 indent = p.ui.getChildIndent();
37007             }
37008             if(this.indentMarkup != indent){ // don't rerender if not required
37009                 this.indentNode.innerHTML = indent;
37010                 this.indentMarkup = indent;
37011             }
37012             this.updateExpandIcon();
37013         }
37014     }
37015 };
37016
37017 Roo.tree.RootTreeNodeUI = function(){
37018     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37019 };
37020 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37021     render : function(){
37022         if(!this.rendered){
37023             var targetNode = this.node.ownerTree.innerCt.dom;
37024             this.node.expanded = true;
37025             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37026             this.wrap = this.ctNode = targetNode.firstChild;
37027         }
37028     },
37029     collapse : function(){
37030     },
37031     expand : function(){
37032     }
37033 });/*
37034  * Based on:
37035  * Ext JS Library 1.1.1
37036  * Copyright(c) 2006-2007, Ext JS, LLC.
37037  *
37038  * Originally Released Under LGPL - original licence link has changed is not relivant.
37039  *
37040  * Fork - LGPL
37041  * <script type="text/javascript">
37042  */
37043 /**
37044  * @class Roo.tree.TreeLoader
37045  * @extends Roo.util.Observable
37046  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37047  * nodes from a specified URL. The response must be a javascript Array definition
37048  * who's elements are node definition objects. eg:
37049  * <pre><code>
37050 {  success : true,
37051    data :      [
37052    
37053     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37054     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37055     ]
37056 }
37057
37058
37059 </code></pre>
37060  * <br><br>
37061  * The old style respose with just an array is still supported, but not recommended.
37062  * <br><br>
37063  *
37064  * A server request is sent, and child nodes are loaded only when a node is expanded.
37065  * The loading node's id is passed to the server under the parameter name "node" to
37066  * enable the server to produce the correct child nodes.
37067  * <br><br>
37068  * To pass extra parameters, an event handler may be attached to the "beforeload"
37069  * event, and the parameters specified in the TreeLoader's baseParams property:
37070  * <pre><code>
37071     myTreeLoader.on("beforeload", function(treeLoader, node) {
37072         this.baseParams.category = node.attributes.category;
37073     }, this);
37074     
37075 </code></pre>
37076  *
37077  * This would pass an HTTP parameter called "category" to the server containing
37078  * the value of the Node's "category" attribute.
37079  * @constructor
37080  * Creates a new Treeloader.
37081  * @param {Object} config A config object containing config properties.
37082  */
37083 Roo.tree.TreeLoader = function(config){
37084     this.baseParams = {};
37085     this.requestMethod = "POST";
37086     Roo.apply(this, config);
37087
37088     this.addEvents({
37089     
37090         /**
37091          * @event beforeload
37092          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37093          * @param {Object} This TreeLoader object.
37094          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37095          * @param {Object} callback The callback function specified in the {@link #load} call.
37096          */
37097         beforeload : true,
37098         /**
37099          * @event load
37100          * Fires when the node has been successfuly loaded.
37101          * @param {Object} This TreeLoader object.
37102          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37103          * @param {Object} response The response object containing the data from the server.
37104          */
37105         load : true,
37106         /**
37107          * @event loadexception
37108          * Fires if the network request failed.
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         loadexception : true,
37114         /**
37115          * @event create
37116          * Fires before a node is created, enabling you to return custom Node types 
37117          * @param {Object} This TreeLoader object.
37118          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37119          */
37120         create : true
37121     });
37122
37123     Roo.tree.TreeLoader.superclass.constructor.call(this);
37124 };
37125
37126 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37127     /**
37128     * @cfg {String} dataUrl The URL from which to request a Json string which
37129     * specifies an array of node definition object representing the child nodes
37130     * to be loaded.
37131     */
37132     /**
37133     * @cfg {String} requestMethod either GET or POST
37134     * defaults to POST (due to BC)
37135     * to be loaded.
37136     */
37137     /**
37138     * @cfg {Object} baseParams (optional) An object containing properties which
37139     * specify HTTP parameters to be passed to each request for child nodes.
37140     */
37141     /**
37142     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37143     * created by this loader. If the attributes sent by the server have an attribute in this object,
37144     * they take priority.
37145     */
37146     /**
37147     * @cfg {Object} uiProviders (optional) An object containing properties which
37148     * 
37149     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37150     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37151     * <i>uiProvider</i> attribute of a returned child node is a string rather
37152     * than a reference to a TreeNodeUI implementation, this that string value
37153     * is used as a property name in the uiProviders object. You can define the provider named
37154     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37155     */
37156     uiProviders : {},
37157
37158     /**
37159     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37160     * child nodes before loading.
37161     */
37162     clearOnLoad : true,
37163
37164     /**
37165     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37166     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37167     * Grid query { data : [ .....] }
37168     */
37169     
37170     root : false,
37171      /**
37172     * @cfg {String} queryParam (optional) 
37173     * Name of the query as it will be passed on the querystring (defaults to 'node')
37174     * eg. the request will be ?node=[id]
37175     */
37176     
37177     
37178     queryParam: false,
37179     
37180     /**
37181      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37182      * This is called automatically when a node is expanded, but may be used to reload
37183      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37184      * @param {Roo.tree.TreeNode} node
37185      * @param {Function} callback
37186      */
37187     load : function(node, callback){
37188         if(this.clearOnLoad){
37189             while(node.firstChild){
37190                 node.removeChild(node.firstChild);
37191             }
37192         }
37193         if(node.attributes.children){ // preloaded json children
37194             var cs = node.attributes.children;
37195             for(var i = 0, len = cs.length; i < len; i++){
37196                 node.appendChild(this.createNode(cs[i]));
37197             }
37198             if(typeof callback == "function"){
37199                 callback();
37200             }
37201         }else if(this.dataUrl){
37202             this.requestData(node, callback);
37203         }
37204     },
37205
37206     getParams: function(node){
37207         var buf = [], bp = this.baseParams;
37208         for(var key in bp){
37209             if(typeof bp[key] != "function"){
37210                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37211             }
37212         }
37213         var n = this.queryParam === false ? 'node' : this.queryParam;
37214         buf.push(n + "=", encodeURIComponent(node.id));
37215         return buf.join("");
37216     },
37217
37218     requestData : function(node, callback){
37219         if(this.fireEvent("beforeload", this, node, callback) !== false){
37220             this.transId = Roo.Ajax.request({
37221                 method:this.requestMethod,
37222                 url: this.dataUrl||this.url,
37223                 success: this.handleResponse,
37224                 failure: this.handleFailure,
37225                 scope: this,
37226                 argument: {callback: callback, node: node},
37227                 params: this.getParams(node)
37228             });
37229         }else{
37230             // if the load is cancelled, make sure we notify
37231             // the node that we are done
37232             if(typeof callback == "function"){
37233                 callback();
37234             }
37235         }
37236     },
37237
37238     isLoading : function(){
37239         return this.transId ? true : false;
37240     },
37241
37242     abort : function(){
37243         if(this.isLoading()){
37244             Roo.Ajax.abort(this.transId);
37245         }
37246     },
37247
37248     // private
37249     createNode : function(attr)
37250     {
37251         // apply baseAttrs, nice idea Corey!
37252         if(this.baseAttrs){
37253             Roo.applyIf(attr, this.baseAttrs);
37254         }
37255         if(this.applyLoader !== false){
37256             attr.loader = this;
37257         }
37258         // uiProvider = depreciated..
37259         
37260         if(typeof(attr.uiProvider) == 'string'){
37261            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37262                 /**  eval:var:attr */ eval(attr.uiProvider);
37263         }
37264         if(typeof(this.uiProviders['default']) != 'undefined') {
37265             attr.uiProvider = this.uiProviders['default'];
37266         }
37267         
37268         this.fireEvent('create', this, attr);
37269         
37270         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37271         return(attr.leaf ?
37272                         new Roo.tree.TreeNode(attr) :
37273                         new Roo.tree.AsyncTreeNode(attr));
37274     },
37275
37276     processResponse : function(response, node, callback)
37277     {
37278         var json = response.responseText;
37279         try {
37280             
37281             var o = Roo.decode(json);
37282             
37283             if (this.root === false && typeof(o.success) != undefined) {
37284                 this.root = 'data'; // the default behaviour for list like data..
37285                 }
37286                 
37287             if (this.root !== false &&  !o.success) {
37288                 // it's a failure condition.
37289                 var a = response.argument;
37290                 this.fireEvent("loadexception", this, a.node, response);
37291                 Roo.log("Load failed - should have a handler really");
37292                 return;
37293             }
37294             
37295             
37296             
37297             if (this.root !== false) {
37298                  o = o[this.root];
37299             }
37300             
37301             for(var i = 0, len = o.length; i < len; i++){
37302                 var n = this.createNode(o[i]);
37303                 if(n){
37304                     node.appendChild(n);
37305                 }
37306             }
37307             if(typeof callback == "function"){
37308                 callback(this, node);
37309             }
37310         }catch(e){
37311             this.handleFailure(response);
37312         }
37313     },
37314
37315     handleResponse : function(response){
37316         this.transId = false;
37317         var a = response.argument;
37318         this.processResponse(response, a.node, a.callback);
37319         this.fireEvent("load", this, a.node, response);
37320     },
37321
37322     handleFailure : function(response)
37323     {
37324         // should handle failure better..
37325         this.transId = false;
37326         var a = response.argument;
37327         this.fireEvent("loadexception", this, a.node, response);
37328         if(typeof a.callback == "function"){
37329             a.callback(this, a.node);
37330         }
37331     }
37332 });/*
37333  * Based on:
37334  * Ext JS Library 1.1.1
37335  * Copyright(c) 2006-2007, Ext JS, LLC.
37336  *
37337  * Originally Released Under LGPL - original licence link has changed is not relivant.
37338  *
37339  * Fork - LGPL
37340  * <script type="text/javascript">
37341  */
37342
37343 /**
37344 * @class Roo.tree.TreeFilter
37345 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37346 * @param {TreePanel} tree
37347 * @param {Object} config (optional)
37348  */
37349 Roo.tree.TreeFilter = function(tree, config){
37350     this.tree = tree;
37351     this.filtered = {};
37352     Roo.apply(this, config);
37353 };
37354
37355 Roo.tree.TreeFilter.prototype = {
37356     clearBlank:false,
37357     reverse:false,
37358     autoClear:false,
37359     remove:false,
37360
37361      /**
37362      * Filter the data by a specific attribute.
37363      * @param {String/RegExp} value Either string that the attribute value
37364      * should start with or a RegExp to test against the attribute
37365      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37366      * @param {TreeNode} startNode (optional) The node to start the filter at.
37367      */
37368     filter : function(value, attr, startNode){
37369         attr = attr || "text";
37370         var f;
37371         if(typeof value == "string"){
37372             var vlen = value.length;
37373             // auto clear empty filter
37374             if(vlen == 0 && this.clearBlank){
37375                 this.clear();
37376                 return;
37377             }
37378             value = value.toLowerCase();
37379             f = function(n){
37380                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37381             };
37382         }else if(value.exec){ // regex?
37383             f = function(n){
37384                 return value.test(n.attributes[attr]);
37385             };
37386         }else{
37387             throw 'Illegal filter type, must be string or regex';
37388         }
37389         this.filterBy(f, null, startNode);
37390         },
37391
37392     /**
37393      * Filter by a function. The passed function will be called with each
37394      * node in the tree (or from the startNode). If the function returns true, the node is kept
37395      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37396      * @param {Function} fn The filter function
37397      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37398      */
37399     filterBy : function(fn, scope, startNode){
37400         startNode = startNode || this.tree.root;
37401         if(this.autoClear){
37402             this.clear();
37403         }
37404         var af = this.filtered, rv = this.reverse;
37405         var f = function(n){
37406             if(n == startNode){
37407                 return true;
37408             }
37409             if(af[n.id]){
37410                 return false;
37411             }
37412             var m = fn.call(scope || n, n);
37413             if(!m || rv){
37414                 af[n.id] = n;
37415                 n.ui.hide();
37416                 return false;
37417             }
37418             return true;
37419         };
37420         startNode.cascade(f);
37421         if(this.remove){
37422            for(var id in af){
37423                if(typeof id != "function"){
37424                    var n = af[id];
37425                    if(n && n.parentNode){
37426                        n.parentNode.removeChild(n);
37427                    }
37428                }
37429            }
37430         }
37431     },
37432
37433     /**
37434      * Clears the current filter. Note: with the "remove" option
37435      * set a filter cannot be cleared.
37436      */
37437     clear : function(){
37438         var t = this.tree;
37439         var af = this.filtered;
37440         for(var id in af){
37441             if(typeof id != "function"){
37442                 var n = af[id];
37443                 if(n){
37444                     n.ui.show();
37445                 }
37446             }
37447         }
37448         this.filtered = {};
37449     }
37450 };
37451 /*
37452  * Based on:
37453  * Ext JS Library 1.1.1
37454  * Copyright(c) 2006-2007, Ext JS, LLC.
37455  *
37456  * Originally Released Under LGPL - original licence link has changed is not relivant.
37457  *
37458  * Fork - LGPL
37459  * <script type="text/javascript">
37460  */
37461  
37462
37463 /**
37464  * @class Roo.tree.TreeSorter
37465  * Provides sorting of nodes in a TreePanel
37466  * 
37467  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37468  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37469  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37470  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37471  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37472  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37473  * @constructor
37474  * @param {TreePanel} tree
37475  * @param {Object} config
37476  */
37477 Roo.tree.TreeSorter = function(tree, config){
37478     Roo.apply(this, config);
37479     tree.on("beforechildrenrendered", this.doSort, this);
37480     tree.on("append", this.updateSort, this);
37481     tree.on("insert", this.updateSort, this);
37482     
37483     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37484     var p = this.property || "text";
37485     var sortType = this.sortType;
37486     var fs = this.folderSort;
37487     var cs = this.caseSensitive === true;
37488     var leafAttr = this.leafAttr || 'leaf';
37489
37490     this.sortFn = function(n1, n2){
37491         if(fs){
37492             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37493                 return 1;
37494             }
37495             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37496                 return -1;
37497             }
37498         }
37499         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37500         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37501         if(v1 < v2){
37502                         return dsc ? +1 : -1;
37503                 }else if(v1 > v2){
37504                         return dsc ? -1 : +1;
37505         }else{
37506                 return 0;
37507         }
37508     };
37509 };
37510
37511 Roo.tree.TreeSorter.prototype = {
37512     doSort : function(node){
37513         node.sort(this.sortFn);
37514     },
37515     
37516     compareNodes : function(n1, n2){
37517         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37518     },
37519     
37520     updateSort : function(tree, node){
37521         if(node.childrenRendered){
37522             this.doSort.defer(1, this, [node]);
37523         }
37524     }
37525 };/*
37526  * Based on:
37527  * Ext JS Library 1.1.1
37528  * Copyright(c) 2006-2007, Ext JS, LLC.
37529  *
37530  * Originally Released Under LGPL - original licence link has changed is not relivant.
37531  *
37532  * Fork - LGPL
37533  * <script type="text/javascript">
37534  */
37535
37536 if(Roo.dd.DropZone){
37537     
37538 Roo.tree.TreeDropZone = function(tree, config){
37539     this.allowParentInsert = false;
37540     this.allowContainerDrop = false;
37541     this.appendOnly = false;
37542     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37543     this.tree = tree;
37544     this.lastInsertClass = "x-tree-no-status";
37545     this.dragOverData = {};
37546 };
37547
37548 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37549     ddGroup : "TreeDD",
37550     scroll:  true,
37551     
37552     expandDelay : 1000,
37553     
37554     expandNode : function(node){
37555         if(node.hasChildNodes() && !node.isExpanded()){
37556             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37557         }
37558     },
37559     
37560     queueExpand : function(node){
37561         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37562     },
37563     
37564     cancelExpand : function(){
37565         if(this.expandProcId){
37566             clearTimeout(this.expandProcId);
37567             this.expandProcId = false;
37568         }
37569     },
37570     
37571     isValidDropPoint : function(n, pt, dd, e, data){
37572         if(!n || !data){ return false; }
37573         var targetNode = n.node;
37574         var dropNode = data.node;
37575         // default drop rules
37576         if(!(targetNode && targetNode.isTarget && pt)){
37577             return false;
37578         }
37579         if(pt == "append" && targetNode.allowChildren === false){
37580             return false;
37581         }
37582         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37583             return false;
37584         }
37585         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37586             return false;
37587         }
37588         // reuse the object
37589         var overEvent = this.dragOverData;
37590         overEvent.tree = this.tree;
37591         overEvent.target = targetNode;
37592         overEvent.data = data;
37593         overEvent.point = pt;
37594         overEvent.source = dd;
37595         overEvent.rawEvent = e;
37596         overEvent.dropNode = dropNode;
37597         overEvent.cancel = false;  
37598         var result = this.tree.fireEvent("nodedragover", overEvent);
37599         return overEvent.cancel === false && result !== false;
37600     },
37601     
37602     getDropPoint : function(e, n, dd)
37603     {
37604         var tn = n.node;
37605         if(tn.isRoot){
37606             return tn.allowChildren !== false ? "append" : false; // always append for root
37607         }
37608         var dragEl = n.ddel;
37609         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37610         var y = Roo.lib.Event.getPageY(e);
37611         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37612         
37613         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37614         var noAppend = tn.allowChildren === false;
37615         if(this.appendOnly || tn.parentNode.allowChildren === false){
37616             return noAppend ? false : "append";
37617         }
37618         var noBelow = false;
37619         if(!this.allowParentInsert){
37620             noBelow = tn.hasChildNodes() && tn.isExpanded();
37621         }
37622         var q = (b - t) / (noAppend ? 2 : 3);
37623         if(y >= t && y < (t + q)){
37624             return "above";
37625         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37626             return "below";
37627         }else{
37628             return "append";
37629         }
37630     },
37631     
37632     onNodeEnter : function(n, dd, e, data)
37633     {
37634         this.cancelExpand();
37635     },
37636     
37637     onNodeOver : function(n, dd, e, data)
37638     {
37639        
37640         var pt = this.getDropPoint(e, n, dd);
37641         var node = n.node;
37642         
37643         // auto node expand check
37644         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37645             this.queueExpand(node);
37646         }else if(pt != "append"){
37647             this.cancelExpand();
37648         }
37649         
37650         // set the insert point style on the target node
37651         var returnCls = this.dropNotAllowed;
37652         if(this.isValidDropPoint(n, pt, dd, e, data)){
37653            if(pt){
37654                var el = n.ddel;
37655                var cls;
37656                if(pt == "above"){
37657                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37658                    cls = "x-tree-drag-insert-above";
37659                }else if(pt == "below"){
37660                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37661                    cls = "x-tree-drag-insert-below";
37662                }else{
37663                    returnCls = "x-tree-drop-ok-append";
37664                    cls = "x-tree-drag-append";
37665                }
37666                if(this.lastInsertClass != cls){
37667                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37668                    this.lastInsertClass = cls;
37669                }
37670            }
37671        }
37672        return returnCls;
37673     },
37674     
37675     onNodeOut : function(n, dd, e, data){
37676         
37677         this.cancelExpand();
37678         this.removeDropIndicators(n);
37679     },
37680     
37681     onNodeDrop : function(n, dd, e, data){
37682         var point = this.getDropPoint(e, n, dd);
37683         var targetNode = n.node;
37684         targetNode.ui.startDrop();
37685         if(!this.isValidDropPoint(n, point, dd, e, data)){
37686             targetNode.ui.endDrop();
37687             return false;
37688         }
37689         // first try to find the drop node
37690         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37691         var dropEvent = {
37692             tree : this.tree,
37693             target: targetNode,
37694             data: data,
37695             point: point,
37696             source: dd,
37697             rawEvent: e,
37698             dropNode: dropNode,
37699             cancel: !dropNode   
37700         };
37701         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37702         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37703             targetNode.ui.endDrop();
37704             return false;
37705         }
37706         // allow target changing
37707         targetNode = dropEvent.target;
37708         if(point == "append" && !targetNode.isExpanded()){
37709             targetNode.expand(false, null, function(){
37710                 this.completeDrop(dropEvent);
37711             }.createDelegate(this));
37712         }else{
37713             this.completeDrop(dropEvent);
37714         }
37715         return true;
37716     },
37717     
37718     completeDrop : function(de){
37719         var ns = de.dropNode, p = de.point, t = de.target;
37720         if(!(ns instanceof Array)){
37721             ns = [ns];
37722         }
37723         var n;
37724         for(var i = 0, len = ns.length; i < len; i++){
37725             n = ns[i];
37726             if(p == "above"){
37727                 t.parentNode.insertBefore(n, t);
37728             }else if(p == "below"){
37729                 t.parentNode.insertBefore(n, t.nextSibling);
37730             }else{
37731                 t.appendChild(n);
37732             }
37733         }
37734         n.ui.focus();
37735         if(this.tree.hlDrop){
37736             n.ui.highlight();
37737         }
37738         t.ui.endDrop();
37739         this.tree.fireEvent("nodedrop", de);
37740     },
37741     
37742     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37743         if(this.tree.hlDrop){
37744             dropNode.ui.focus();
37745             dropNode.ui.highlight();
37746         }
37747         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37748     },
37749     
37750     getTree : function(){
37751         return this.tree;
37752     },
37753     
37754     removeDropIndicators : function(n){
37755         if(n && n.ddel){
37756             var el = n.ddel;
37757             Roo.fly(el).removeClass([
37758                     "x-tree-drag-insert-above",
37759                     "x-tree-drag-insert-below",
37760                     "x-tree-drag-append"]);
37761             this.lastInsertClass = "_noclass";
37762         }
37763     },
37764     
37765     beforeDragDrop : function(target, e, id){
37766         this.cancelExpand();
37767         return true;
37768     },
37769     
37770     afterRepair : function(data){
37771         if(data && Roo.enableFx){
37772             data.node.ui.highlight();
37773         }
37774         this.hideProxy();
37775     } 
37776     
37777 });
37778
37779 }
37780 /*
37781  * Based on:
37782  * Ext JS Library 1.1.1
37783  * Copyright(c) 2006-2007, Ext JS, LLC.
37784  *
37785  * Originally Released Under LGPL - original licence link has changed is not relivant.
37786  *
37787  * Fork - LGPL
37788  * <script type="text/javascript">
37789  */
37790  
37791
37792 if(Roo.dd.DragZone){
37793 Roo.tree.TreeDragZone = function(tree, config){
37794     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37795     this.tree = tree;
37796 };
37797
37798 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37799     ddGroup : "TreeDD",
37800    
37801     onBeforeDrag : function(data, e){
37802         var n = data.node;
37803         return n && n.draggable && !n.disabled;
37804     },
37805      
37806     
37807     onInitDrag : function(e){
37808         var data = this.dragData;
37809         this.tree.getSelectionModel().select(data.node);
37810         this.proxy.update("");
37811         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37812         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37813     },
37814     
37815     getRepairXY : function(e, data){
37816         return data.node.ui.getDDRepairXY();
37817     },
37818     
37819     onEndDrag : function(data, e){
37820         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37821         
37822         
37823     },
37824     
37825     onValidDrop : function(dd, e, id){
37826         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37827         this.hideProxy();
37828     },
37829     
37830     beforeInvalidDrop : function(e, id){
37831         // this scrolls the original position back into view
37832         var sm = this.tree.getSelectionModel();
37833         sm.clearSelections();
37834         sm.select(this.dragData.node);
37835     }
37836 });
37837 }/*
37838  * Based on:
37839  * Ext JS Library 1.1.1
37840  * Copyright(c) 2006-2007, Ext JS, LLC.
37841  *
37842  * Originally Released Under LGPL - original licence link has changed is not relivant.
37843  *
37844  * Fork - LGPL
37845  * <script type="text/javascript">
37846  */
37847 /**
37848  * @class Roo.tree.TreeEditor
37849  * @extends Roo.Editor
37850  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37851  * as the editor field.
37852  * @constructor
37853  * @param {Object} config (used to be the tree panel.)
37854  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37855  * 
37856  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37857  * @cfg {Roo.form.TextField} field [required] The field configuration
37858  *
37859  * 
37860  */
37861 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37862     var tree = config;
37863     var field;
37864     if (oldconfig) { // old style..
37865         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37866     } else {
37867         // new style..
37868         tree = config.tree;
37869         config.field = config.field  || {};
37870         config.field.xtype = 'TextField';
37871         field = Roo.factory(config.field, Roo.form);
37872     }
37873     config = config || {};
37874     
37875     
37876     this.addEvents({
37877         /**
37878          * @event beforenodeedit
37879          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37880          * false from the handler of this event.
37881          * @param {Editor} this
37882          * @param {Roo.tree.Node} node 
37883          */
37884         "beforenodeedit" : true
37885     });
37886     
37887     //Roo.log(config);
37888     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37889
37890     this.tree = tree;
37891
37892     tree.on('beforeclick', this.beforeNodeClick, this);
37893     tree.getTreeEl().on('mousedown', this.hide, this);
37894     this.on('complete', this.updateNode, this);
37895     this.on('beforestartedit', this.fitToTree, this);
37896     this.on('startedit', this.bindScroll, this, {delay:10});
37897     this.on('specialkey', this.onSpecialKey, this);
37898 };
37899
37900 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37901     /**
37902      * @cfg {String} alignment
37903      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37904      */
37905     alignment: "l-l",
37906     // inherit
37907     autoSize: false,
37908     /**
37909      * @cfg {Boolean} hideEl
37910      * True to hide the bound element while the editor is displayed (defaults to false)
37911      */
37912     hideEl : false,
37913     /**
37914      * @cfg {String} cls
37915      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37916      */
37917     cls: "x-small-editor x-tree-editor",
37918     /**
37919      * @cfg {Boolean} shim
37920      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37921      */
37922     shim:false,
37923     // inherit
37924     shadow:"frame",
37925     /**
37926      * @cfg {Number} maxWidth
37927      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37928      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37929      * scroll and client offsets into account prior to each edit.
37930      */
37931     maxWidth: 250,
37932
37933     editDelay : 350,
37934
37935     // private
37936     fitToTree : function(ed, el){
37937         var td = this.tree.getTreeEl().dom, nd = el.dom;
37938         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37939             td.scrollLeft = nd.offsetLeft;
37940         }
37941         var w = Math.min(
37942                 this.maxWidth,
37943                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37944         this.setSize(w, '');
37945         
37946         return this.fireEvent('beforenodeedit', this, this.editNode);
37947         
37948     },
37949
37950     // private
37951     triggerEdit : function(node){
37952         this.completeEdit();
37953         this.editNode = node;
37954         this.startEdit(node.ui.textNode, node.text);
37955     },
37956
37957     // private
37958     bindScroll : function(){
37959         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37960     },
37961
37962     // private
37963     beforeNodeClick : function(node, e){
37964         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37965         this.lastClick = new Date();
37966         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37967             e.stopEvent();
37968             this.triggerEdit(node);
37969             return false;
37970         }
37971         return true;
37972     },
37973
37974     // private
37975     updateNode : function(ed, value){
37976         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37977         this.editNode.setText(value);
37978     },
37979
37980     // private
37981     onHide : function(){
37982         Roo.tree.TreeEditor.superclass.onHide.call(this);
37983         if(this.editNode){
37984             this.editNode.ui.focus();
37985         }
37986     },
37987
37988     // private
37989     onSpecialKey : function(field, e){
37990         var k = e.getKey();
37991         if(k == e.ESC){
37992             e.stopEvent();
37993             this.cancelEdit();
37994         }else if(k == e.ENTER && !e.hasModifier()){
37995             e.stopEvent();
37996             this.completeEdit();
37997         }
37998     }
37999 });//<Script type="text/javascript">
38000 /*
38001  * Based on:
38002  * Ext JS Library 1.1.1
38003  * Copyright(c) 2006-2007, Ext JS, LLC.
38004  *
38005  * Originally Released Under LGPL - original licence link has changed is not relivant.
38006  *
38007  * Fork - LGPL
38008  * <script type="text/javascript">
38009  */
38010  
38011 /**
38012  * Not documented??? - probably should be...
38013  */
38014
38015 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38016     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38017     
38018     renderElements : function(n, a, targetNode, bulkRender){
38019         //consel.log("renderElements?");
38020         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38021
38022         var t = n.getOwnerTree();
38023         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38024         
38025         var cols = t.columns;
38026         var bw = t.borderWidth;
38027         var c = cols[0];
38028         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38029          var cb = typeof a.checked == "boolean";
38030         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38031         var colcls = 'x-t-' + tid + '-c0';
38032         var buf = [
38033             '<li class="x-tree-node">',
38034             
38035                 
38036                 '<div class="x-tree-node-el ', a.cls,'">',
38037                     // extran...
38038                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38039                 
38040                 
38041                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38042                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38043                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38044                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38045                            (a.iconCls ? ' '+a.iconCls : ''),
38046                            '" unselectable="on" />',
38047                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38048                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38049                              
38050                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38051                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38052                             '<span unselectable="on" qtip="' + tx + '">',
38053                              tx,
38054                              '</span></a>' ,
38055                     '</div>',
38056                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38057                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38058                  ];
38059         for(var i = 1, len = cols.length; i < len; i++){
38060             c = cols[i];
38061             colcls = 'x-t-' + tid + '-c' +i;
38062             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38063             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38064                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38065                       "</div>");
38066          }
38067          
38068          buf.push(
38069             '</a>',
38070             '<div class="x-clear"></div></div>',
38071             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38072             "</li>");
38073         
38074         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38075             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38076                                 n.nextSibling.ui.getEl(), buf.join(""));
38077         }else{
38078             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38079         }
38080         var el = this.wrap.firstChild;
38081         this.elRow = el;
38082         this.elNode = el.firstChild;
38083         this.ranchor = el.childNodes[1];
38084         this.ctNode = this.wrap.childNodes[1];
38085         var cs = el.firstChild.childNodes;
38086         this.indentNode = cs[0];
38087         this.ecNode = cs[1];
38088         this.iconNode = cs[2];
38089         var index = 3;
38090         if(cb){
38091             this.checkbox = cs[3];
38092             index++;
38093         }
38094         this.anchor = cs[index];
38095         
38096         this.textNode = cs[index].firstChild;
38097         
38098         //el.on("click", this.onClick, this);
38099         //el.on("dblclick", this.onDblClick, this);
38100         
38101         
38102        // console.log(this);
38103     },
38104     initEvents : function(){
38105         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38106         
38107             
38108         var a = this.ranchor;
38109
38110         var el = Roo.get(a);
38111
38112         if(Roo.isOpera){ // opera render bug ignores the CSS
38113             el.setStyle("text-decoration", "none");
38114         }
38115
38116         el.on("click", this.onClick, this);
38117         el.on("dblclick", this.onDblClick, this);
38118         el.on("contextmenu", this.onContextMenu, this);
38119         
38120     },
38121     
38122     /*onSelectedChange : function(state){
38123         if(state){
38124             this.focus();
38125             this.addClass("x-tree-selected");
38126         }else{
38127             //this.blur();
38128             this.removeClass("x-tree-selected");
38129         }
38130     },*/
38131     addClass : function(cls){
38132         if(this.elRow){
38133             Roo.fly(this.elRow).addClass(cls);
38134         }
38135         
38136     },
38137     
38138     
38139     removeClass : function(cls){
38140         if(this.elRow){
38141             Roo.fly(this.elRow).removeClass(cls);
38142         }
38143     }
38144
38145     
38146     
38147 });//<Script type="text/javascript">
38148
38149 /*
38150  * Based on:
38151  * Ext JS Library 1.1.1
38152  * Copyright(c) 2006-2007, Ext JS, LLC.
38153  *
38154  * Originally Released Under LGPL - original licence link has changed is not relivant.
38155  *
38156  * Fork - LGPL
38157  * <script type="text/javascript">
38158  */
38159  
38160
38161 /**
38162  * @class Roo.tree.ColumnTree
38163  * @extends Roo.tree.TreePanel
38164  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38165  * @cfg {int} borderWidth  compined right/left border allowance
38166  * @constructor
38167  * @param {String/HTMLElement/Element} el The container element
38168  * @param {Object} config
38169  */
38170 Roo.tree.ColumnTree =  function(el, config)
38171 {
38172    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38173    this.addEvents({
38174         /**
38175         * @event resize
38176         * Fire this event on a container when it resizes
38177         * @param {int} w Width
38178         * @param {int} h Height
38179         */
38180        "resize" : true
38181     });
38182     this.on('resize', this.onResize, this);
38183 };
38184
38185 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38186     //lines:false,
38187     
38188     
38189     borderWidth: Roo.isBorderBox ? 0 : 2, 
38190     headEls : false,
38191     
38192     render : function(){
38193         // add the header.....
38194        
38195         Roo.tree.ColumnTree.superclass.render.apply(this);
38196         
38197         this.el.addClass('x-column-tree');
38198         
38199         this.headers = this.el.createChild(
38200             {cls:'x-tree-headers'},this.innerCt.dom);
38201    
38202         var cols = this.columns, c;
38203         var totalWidth = 0;
38204         this.headEls = [];
38205         var  len = cols.length;
38206         for(var i = 0; i < len; i++){
38207              c = cols[i];
38208              totalWidth += c.width;
38209             this.headEls.push(this.headers.createChild({
38210                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38211                  cn: {
38212                      cls:'x-tree-hd-text',
38213                      html: c.header
38214                  },
38215                  style:'width:'+(c.width-this.borderWidth)+'px;'
38216              }));
38217         }
38218         this.headers.createChild({cls:'x-clear'});
38219         // prevent floats from wrapping when clipped
38220         this.headers.setWidth(totalWidth);
38221         //this.innerCt.setWidth(totalWidth);
38222         this.innerCt.setStyle({ overflow: 'auto' });
38223         this.onResize(this.width, this.height);
38224              
38225         
38226     },
38227     onResize : function(w,h)
38228     {
38229         this.height = h;
38230         this.width = w;
38231         // resize cols..
38232         this.innerCt.setWidth(this.width);
38233         this.innerCt.setHeight(this.height-20);
38234         
38235         // headers...
38236         var cols = this.columns, c;
38237         var totalWidth = 0;
38238         var expEl = false;
38239         var len = cols.length;
38240         for(var i = 0; i < len; i++){
38241             c = cols[i];
38242             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38243                 // it's the expander..
38244                 expEl  = this.headEls[i];
38245                 continue;
38246             }
38247             totalWidth += c.width;
38248             
38249         }
38250         if (expEl) {
38251             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38252         }
38253         this.headers.setWidth(w-20);
38254
38255         
38256         
38257         
38258     }
38259 });
38260 /*
38261  * Based on:
38262  * Ext JS Library 1.1.1
38263  * Copyright(c) 2006-2007, Ext JS, LLC.
38264  *
38265  * Originally Released Under LGPL - original licence link has changed is not relivant.
38266  *
38267  * Fork - LGPL
38268  * <script type="text/javascript">
38269  */
38270  
38271 /**
38272  * @class Roo.menu.Menu
38273  * @extends Roo.util.Observable
38274  * @children Roo.menu.BaseItem
38275  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38276  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38277  * @constructor
38278  * Creates a new Menu
38279  * @param {Object} config Configuration options
38280  */
38281 Roo.menu.Menu = function(config){
38282     
38283     Roo.menu.Menu.superclass.constructor.call(this, config);
38284     
38285     this.id = this.id || Roo.id();
38286     this.addEvents({
38287         /**
38288          * @event beforeshow
38289          * Fires before this menu is displayed
38290          * @param {Roo.menu.Menu} this
38291          */
38292         beforeshow : true,
38293         /**
38294          * @event beforehide
38295          * Fires before this menu is hidden
38296          * @param {Roo.menu.Menu} this
38297          */
38298         beforehide : true,
38299         /**
38300          * @event show
38301          * Fires after this menu is displayed
38302          * @param {Roo.menu.Menu} this
38303          */
38304         show : true,
38305         /**
38306          * @event hide
38307          * Fires after this menu is hidden
38308          * @param {Roo.menu.Menu} this
38309          */
38310         hide : true,
38311         /**
38312          * @event click
38313          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38314          * @param {Roo.menu.Menu} this
38315          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38316          * @param {Roo.EventObject} e
38317          */
38318         click : true,
38319         /**
38320          * @event mouseover
38321          * Fires when the mouse is hovering over this menu
38322          * @param {Roo.menu.Menu} this
38323          * @param {Roo.EventObject} e
38324          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38325          */
38326         mouseover : true,
38327         /**
38328          * @event mouseout
38329          * Fires when the mouse exits 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         mouseout : true,
38335         /**
38336          * @event itemclick
38337          * Fires when a menu item contained in this menu is clicked
38338          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38339          * @param {Roo.EventObject} e
38340          */
38341         itemclick: true
38342     });
38343     if (this.registerMenu) {
38344         Roo.menu.MenuMgr.register(this);
38345     }
38346     
38347     var mis = this.items;
38348     this.items = new Roo.util.MixedCollection();
38349     if(mis){
38350         this.add.apply(this, mis);
38351     }
38352 };
38353
38354 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38355     /**
38356      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38357      */
38358     minWidth : 120,
38359     /**
38360      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38361      * for bottom-right shadow (defaults to "sides")
38362      */
38363     shadow : "sides",
38364     /**
38365      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38366      * this menu (defaults to "tl-tr?")
38367      */
38368     subMenuAlign : "tl-tr?",
38369     /**
38370      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38371      * relative to its element of origin (defaults to "tl-bl?")
38372      */
38373     defaultAlign : "tl-bl?",
38374     /**
38375      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38376      */
38377     allowOtherMenus : false,
38378     /**
38379      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38380      */
38381     registerMenu : true,
38382
38383     hidden:true,
38384
38385     // private
38386     render : function(){
38387         if(this.el){
38388             return;
38389         }
38390         var el = this.el = new Roo.Layer({
38391             cls: "x-menu",
38392             shadow:this.shadow,
38393             constrain: false,
38394             parentEl: this.parentEl || document.body,
38395             zindex:15000
38396         });
38397
38398         this.keyNav = new Roo.menu.MenuNav(this);
38399
38400         if(this.plain){
38401             el.addClass("x-menu-plain");
38402         }
38403         if(this.cls){
38404             el.addClass(this.cls);
38405         }
38406         // generic focus element
38407         this.focusEl = el.createChild({
38408             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38409         });
38410         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38411         //disabling touch- as it's causing issues ..
38412         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38413         ul.on('click'   , this.onClick, this);
38414         
38415         
38416         ul.on("mouseover", this.onMouseOver, this);
38417         ul.on("mouseout", this.onMouseOut, this);
38418         this.items.each(function(item){
38419             if (item.hidden) {
38420                 return;
38421             }
38422             
38423             var li = document.createElement("li");
38424             li.className = "x-menu-list-item";
38425             ul.dom.appendChild(li);
38426             item.render(li, this);
38427         }, this);
38428         this.ul = ul;
38429         this.autoWidth();
38430     },
38431
38432     // private
38433     autoWidth : function(){
38434         var el = this.el, ul = this.ul;
38435         if(!el){
38436             return;
38437         }
38438         var w = this.width;
38439         if(w){
38440             el.setWidth(w);
38441         }else if(Roo.isIE){
38442             el.setWidth(this.minWidth);
38443             var t = el.dom.offsetWidth; // force recalc
38444             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38445         }
38446     },
38447
38448     // private
38449     delayAutoWidth : function(){
38450         if(this.rendered){
38451             if(!this.awTask){
38452                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38453             }
38454             this.awTask.delay(20);
38455         }
38456     },
38457
38458     // private
38459     findTargetItem : function(e){
38460         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38461         if(t && t.menuItemId){
38462             return this.items.get(t.menuItemId);
38463         }
38464     },
38465
38466     // private
38467     onClick : function(e){
38468         Roo.log("menu.onClick");
38469         var t = this.findTargetItem(e);
38470         if(!t){
38471             return;
38472         }
38473         Roo.log(e);
38474         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38475             if(t == this.activeItem && t.shouldDeactivate(e)){
38476                 this.activeItem.deactivate();
38477                 delete this.activeItem;
38478                 return;
38479             }
38480             if(t.canActivate){
38481                 this.setActiveItem(t, true);
38482             }
38483             return;
38484             
38485             
38486         }
38487         
38488         t.onClick(e);
38489         this.fireEvent("click", this, t, e);
38490     },
38491
38492     // private
38493     setActiveItem : function(item, autoExpand){
38494         if(item != this.activeItem){
38495             if(this.activeItem){
38496                 this.activeItem.deactivate();
38497             }
38498             this.activeItem = item;
38499             item.activate(autoExpand);
38500         }else if(autoExpand){
38501             item.expandMenu();
38502         }
38503     },
38504
38505     // private
38506     tryActivate : function(start, step){
38507         var items = this.items;
38508         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38509             var item = items.get(i);
38510             if(!item.disabled && item.canActivate){
38511                 this.setActiveItem(item, false);
38512                 return item;
38513             }
38514         }
38515         return false;
38516     },
38517
38518     // private
38519     onMouseOver : function(e){
38520         var t;
38521         if(t = this.findTargetItem(e)){
38522             if(t.canActivate && !t.disabled){
38523                 this.setActiveItem(t, true);
38524             }
38525         }
38526         this.fireEvent("mouseover", this, e, t);
38527     },
38528
38529     // private
38530     onMouseOut : function(e){
38531         var t;
38532         if(t = this.findTargetItem(e)){
38533             if(t == this.activeItem && t.shouldDeactivate(e)){
38534                 this.activeItem.deactivate();
38535                 delete this.activeItem;
38536             }
38537         }
38538         this.fireEvent("mouseout", this, e, t);
38539     },
38540
38541     /**
38542      * Read-only.  Returns true if the menu is currently displayed, else false.
38543      * @type Boolean
38544      */
38545     isVisible : function(){
38546         return this.el && !this.hidden;
38547     },
38548
38549     /**
38550      * Displays this menu relative to another element
38551      * @param {String/HTMLElement/Roo.Element} element The element to align to
38552      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38553      * the element (defaults to this.defaultAlign)
38554      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38555      */
38556     show : function(el, pos, parentMenu){
38557         this.parentMenu = parentMenu;
38558         if(!this.el){
38559             this.render();
38560         }
38561         this.fireEvent("beforeshow", this);
38562         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38563     },
38564
38565     /**
38566      * Displays this menu at a specific xy position
38567      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38568      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38569      */
38570     showAt : function(xy, parentMenu, /* private: */_e){
38571         this.parentMenu = parentMenu;
38572         if(!this.el){
38573             this.render();
38574         }
38575         if(_e !== false){
38576             this.fireEvent("beforeshow", this);
38577             xy = this.el.adjustForConstraints(xy);
38578         }
38579         this.el.setXY(xy);
38580         this.el.show();
38581         this.hidden = false;
38582         this.focus();
38583         this.fireEvent("show", this);
38584     },
38585
38586     focus : function(){
38587         if(!this.hidden){
38588             this.doFocus.defer(50, this);
38589         }
38590     },
38591
38592     doFocus : function(){
38593         if(!this.hidden){
38594             this.focusEl.focus();
38595         }
38596     },
38597
38598     /**
38599      * Hides this menu and optionally all parent menus
38600      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38601      */
38602     hide : function(deep){
38603         if(this.el && this.isVisible()){
38604             this.fireEvent("beforehide", this);
38605             if(this.activeItem){
38606                 this.activeItem.deactivate();
38607                 this.activeItem = null;
38608             }
38609             this.el.hide();
38610             this.hidden = true;
38611             this.fireEvent("hide", this);
38612         }
38613         if(deep === true && this.parentMenu){
38614             this.parentMenu.hide(true);
38615         }
38616     },
38617
38618     /**
38619      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38620      * Any of the following are valid:
38621      * <ul>
38622      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38623      * <li>An HTMLElement object which will be converted to a menu item</li>
38624      * <li>A menu item config object that will be created as a new menu item</li>
38625      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38626      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38627      * </ul>
38628      * Usage:
38629      * <pre><code>
38630 // Create the menu
38631 var menu = new Roo.menu.Menu();
38632
38633 // Create a menu item to add by reference
38634 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38635
38636 // Add a bunch of items at once using different methods.
38637 // Only the last item added will be returned.
38638 var item = menu.add(
38639     menuItem,                // add existing item by ref
38640     'Dynamic Item',          // new TextItem
38641     '-',                     // new separator
38642     { text: 'Config Item' }  // new item by config
38643 );
38644 </code></pre>
38645      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38646      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38647      */
38648     add : function(){
38649         var a = arguments, l = a.length, item;
38650         for(var i = 0; i < l; i++){
38651             var el = a[i];
38652             if ((typeof(el) == "object") && el.xtype && el.xns) {
38653                 el = Roo.factory(el, Roo.menu);
38654             }
38655             
38656             if(el.render){ // some kind of Item
38657                 item = this.addItem(el);
38658             }else if(typeof el == "string"){ // string
38659                 if(el == "separator" || el == "-"){
38660                     item = this.addSeparator();
38661                 }else{
38662                     item = this.addText(el);
38663                 }
38664             }else if(el.tagName || el.el){ // element
38665                 item = this.addElement(el);
38666             }else if(typeof el == "object"){ // must be menu item config?
38667                 item = this.addMenuItem(el);
38668             }
38669         }
38670         return item;
38671     },
38672
38673     /**
38674      * Returns this menu's underlying {@link Roo.Element} object
38675      * @return {Roo.Element} The element
38676      */
38677     getEl : function(){
38678         if(!this.el){
38679             this.render();
38680         }
38681         return this.el;
38682     },
38683
38684     /**
38685      * Adds a separator bar to the menu
38686      * @return {Roo.menu.Item} The menu item that was added
38687      */
38688     addSeparator : function(){
38689         return this.addItem(new Roo.menu.Separator());
38690     },
38691
38692     /**
38693      * Adds an {@link Roo.Element} object to the menu
38694      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38695      * @return {Roo.menu.Item} The menu item that was added
38696      */
38697     addElement : function(el){
38698         return this.addItem(new Roo.menu.BaseItem(el));
38699     },
38700
38701     /**
38702      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38703      * @param {Roo.menu.Item} item The menu item to add
38704      * @return {Roo.menu.Item} The menu item that was added
38705      */
38706     addItem : function(item){
38707         this.items.add(item);
38708         if(this.ul){
38709             var li = document.createElement("li");
38710             li.className = "x-menu-list-item";
38711             this.ul.dom.appendChild(li);
38712             item.render(li, this);
38713             this.delayAutoWidth();
38714         }
38715         return item;
38716     },
38717
38718     /**
38719      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38720      * @param {Object} config A MenuItem config object
38721      * @return {Roo.menu.Item} The menu item that was added
38722      */
38723     addMenuItem : function(config){
38724         if(!(config instanceof Roo.menu.Item)){
38725             if(typeof config.checked == "boolean"){ // must be check menu item config?
38726                 config = new Roo.menu.CheckItem(config);
38727             }else{
38728                 config = new Roo.menu.Item(config);
38729             }
38730         }
38731         return this.addItem(config);
38732     },
38733
38734     /**
38735      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38736      * @param {String} text The text to display in the menu item
38737      * @return {Roo.menu.Item} The menu item that was added
38738      */
38739     addText : function(text){
38740         return this.addItem(new Roo.menu.TextItem({ text : text }));
38741     },
38742
38743     /**
38744      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38745      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38746      * @param {Roo.menu.Item} item The menu item to add
38747      * @return {Roo.menu.Item} The menu item that was added
38748      */
38749     insert : function(index, item){
38750         this.items.insert(index, item);
38751         if(this.ul){
38752             var li = document.createElement("li");
38753             li.className = "x-menu-list-item";
38754             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38755             item.render(li, this);
38756             this.delayAutoWidth();
38757         }
38758         return item;
38759     },
38760
38761     /**
38762      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38763      * @param {Roo.menu.Item} item The menu item to remove
38764      */
38765     remove : function(item){
38766         this.items.removeKey(item.id);
38767         item.destroy();
38768     },
38769
38770     /**
38771      * Removes and destroys all items in the menu
38772      */
38773     removeAll : function(){
38774         var f;
38775         while(f = this.items.first()){
38776             this.remove(f);
38777         }
38778     }
38779 });
38780
38781 // MenuNav is a private utility class used internally by the Menu
38782 Roo.menu.MenuNav = function(menu){
38783     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38784     this.scope = this.menu = menu;
38785 };
38786
38787 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38788     doRelay : function(e, h){
38789         var k = e.getKey();
38790         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38791             this.menu.tryActivate(0, 1);
38792             return false;
38793         }
38794         return h.call(this.scope || this, e, this.menu);
38795     },
38796
38797     up : function(e, m){
38798         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38799             m.tryActivate(m.items.length-1, -1);
38800         }
38801     },
38802
38803     down : function(e, m){
38804         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38805             m.tryActivate(0, 1);
38806         }
38807     },
38808
38809     right : function(e, m){
38810         if(m.activeItem){
38811             m.activeItem.expandMenu(true);
38812         }
38813     },
38814
38815     left : function(e, m){
38816         m.hide();
38817         if(m.parentMenu && m.parentMenu.activeItem){
38818             m.parentMenu.activeItem.activate();
38819         }
38820     },
38821
38822     enter : function(e, m){
38823         if(m.activeItem){
38824             e.stopPropagation();
38825             m.activeItem.onClick(e);
38826             m.fireEvent("click", this, m.activeItem);
38827             return true;
38828         }
38829     }
38830 });/*
38831  * Based on:
38832  * Ext JS Library 1.1.1
38833  * Copyright(c) 2006-2007, Ext JS, LLC.
38834  *
38835  * Originally Released Under LGPL - original licence link has changed is not relivant.
38836  *
38837  * Fork - LGPL
38838  * <script type="text/javascript">
38839  */
38840  
38841 /**
38842  * @class Roo.menu.MenuMgr
38843  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38844  * @static
38845  */
38846 Roo.menu.MenuMgr = function(){
38847    var menus, active, groups = {}, attached = false, lastShow = new Date();
38848
38849    // private - called when first menu is created
38850    function init(){
38851        menus = {};
38852        active = new Roo.util.MixedCollection();
38853        Roo.get(document).addKeyListener(27, function(){
38854            if(active.length > 0){
38855                hideAll();
38856            }
38857        });
38858    }
38859
38860    // private
38861    function hideAll(){
38862        if(active && active.length > 0){
38863            var c = active.clone();
38864            c.each(function(m){
38865                m.hide();
38866            });
38867        }
38868    }
38869
38870    // private
38871    function onHide(m){
38872        active.remove(m);
38873        if(active.length < 1){
38874            Roo.get(document).un("mousedown", onMouseDown);
38875            attached = false;
38876        }
38877    }
38878
38879    // private
38880    function onShow(m){
38881        var last = active.last();
38882        lastShow = new Date();
38883        active.add(m);
38884        if(!attached){
38885            Roo.get(document).on("mousedown", onMouseDown);
38886            attached = true;
38887        }
38888        if(m.parentMenu){
38889           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38890           m.parentMenu.activeChild = m;
38891        }else if(last && last.isVisible()){
38892           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38893        }
38894    }
38895
38896    // private
38897    function onBeforeHide(m){
38898        if(m.activeChild){
38899            m.activeChild.hide();
38900        }
38901        if(m.autoHideTimer){
38902            clearTimeout(m.autoHideTimer);
38903            delete m.autoHideTimer;
38904        }
38905    }
38906
38907    // private
38908    function onBeforeShow(m){
38909        var pm = m.parentMenu;
38910        if(!pm && !m.allowOtherMenus){
38911            hideAll();
38912        }else if(pm && pm.activeChild && active != m){
38913            pm.activeChild.hide();
38914        }
38915    }
38916
38917    // private
38918    function onMouseDown(e){
38919        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38920            hideAll();
38921        }
38922    }
38923
38924    // private
38925    function onBeforeCheck(mi, state){
38926        if(state){
38927            var g = groups[mi.group];
38928            for(var i = 0, l = g.length; i < l; i++){
38929                if(g[i] != mi){
38930                    g[i].setChecked(false);
38931                }
38932            }
38933        }
38934    }
38935
38936    return {
38937
38938        /**
38939         * Hides all menus that are currently visible
38940         */
38941        hideAll : function(){
38942             hideAll();  
38943        },
38944
38945        // private
38946        register : function(menu){
38947            if(!menus){
38948                init();
38949            }
38950            menus[menu.id] = menu;
38951            menu.on("beforehide", onBeforeHide);
38952            menu.on("hide", onHide);
38953            menu.on("beforeshow", onBeforeShow);
38954            menu.on("show", onShow);
38955            var g = menu.group;
38956            if(g && menu.events["checkchange"]){
38957                if(!groups[g]){
38958                    groups[g] = [];
38959                }
38960                groups[g].push(menu);
38961                menu.on("checkchange", onCheck);
38962            }
38963        },
38964
38965         /**
38966          * Returns a {@link Roo.menu.Menu} object
38967          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38968          * be used to generate and return a new Menu instance.
38969          */
38970        get : function(menu){
38971            if(typeof menu == "string"){ // menu id
38972                return menus[menu];
38973            }else if(menu.events){  // menu instance
38974                return menu;
38975            }else if(typeof menu.length == 'number'){ // array of menu items?
38976                return new Roo.menu.Menu({items:menu});
38977            }else{ // otherwise, must be a config
38978                return new Roo.menu.Menu(menu);
38979            }
38980        },
38981
38982        // private
38983        unregister : function(menu){
38984            delete menus[menu.id];
38985            menu.un("beforehide", onBeforeHide);
38986            menu.un("hide", onHide);
38987            menu.un("beforeshow", onBeforeShow);
38988            menu.un("show", onShow);
38989            var g = menu.group;
38990            if(g && menu.events["checkchange"]){
38991                groups[g].remove(menu);
38992                menu.un("checkchange", onCheck);
38993            }
38994        },
38995
38996        // private
38997        registerCheckable : function(menuItem){
38998            var g = menuItem.group;
38999            if(g){
39000                if(!groups[g]){
39001                    groups[g] = [];
39002                }
39003                groups[g].push(menuItem);
39004                menuItem.on("beforecheckchange", onBeforeCheck);
39005            }
39006        },
39007
39008        // private
39009        unregisterCheckable : function(menuItem){
39010            var g = menuItem.group;
39011            if(g){
39012                groups[g].remove(menuItem);
39013                menuItem.un("beforecheckchange", onBeforeCheck);
39014            }
39015        }
39016    };
39017 }();/*
39018  * Based on:
39019  * Ext JS Library 1.1.1
39020  * Copyright(c) 2006-2007, Ext JS, LLC.
39021  *
39022  * Originally Released Under LGPL - original licence link has changed is not relivant.
39023  *
39024  * Fork - LGPL
39025  * <script type="text/javascript">
39026  */
39027  
39028
39029 /**
39030  * @class Roo.menu.BaseItem
39031  * @extends Roo.Component
39032  * @abstract
39033  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39034  * management and base configuration options shared by all menu components.
39035  * @constructor
39036  * Creates a new BaseItem
39037  * @param {Object} config Configuration options
39038  */
39039 Roo.menu.BaseItem = function(config){
39040     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39041
39042     this.addEvents({
39043         /**
39044          * @event click
39045          * Fires when this item is clicked
39046          * @param {Roo.menu.BaseItem} this
39047          * @param {Roo.EventObject} e
39048          */
39049         click: true,
39050         /**
39051          * @event activate
39052          * Fires when this item is activated
39053          * @param {Roo.menu.BaseItem} this
39054          */
39055         activate : true,
39056         /**
39057          * @event deactivate
39058          * Fires when this item is deactivated
39059          * @param {Roo.menu.BaseItem} this
39060          */
39061         deactivate : true
39062     });
39063
39064     if(this.handler){
39065         this.on("click", this.handler, this.scope, true);
39066     }
39067 };
39068
39069 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39070     /**
39071      * @cfg {Function} handler
39072      * A function that will handle the click event of this menu item (defaults to undefined)
39073      */
39074     /**
39075      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39076      */
39077     canActivate : false,
39078     
39079      /**
39080      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39081      */
39082     hidden: false,
39083     
39084     /**
39085      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39086      */
39087     activeClass : "x-menu-item-active",
39088     /**
39089      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39090      */
39091     hideOnClick : true,
39092     /**
39093      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39094      */
39095     hideDelay : 100,
39096
39097     // private
39098     ctype: "Roo.menu.BaseItem",
39099
39100     // private
39101     actionMode : "container",
39102
39103     // private
39104     render : function(container, parentMenu){
39105         this.parentMenu = parentMenu;
39106         Roo.menu.BaseItem.superclass.render.call(this, container);
39107         this.container.menuItemId = this.id;
39108     },
39109
39110     // private
39111     onRender : function(container, position){
39112         this.el = Roo.get(this.el);
39113         container.dom.appendChild(this.el.dom);
39114     },
39115
39116     // private
39117     onClick : function(e){
39118         if(!this.disabled && this.fireEvent("click", this, e) !== false
39119                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39120             this.handleClick(e);
39121         }else{
39122             e.stopEvent();
39123         }
39124     },
39125
39126     // private
39127     activate : function(){
39128         if(this.disabled){
39129             return false;
39130         }
39131         var li = this.container;
39132         li.addClass(this.activeClass);
39133         this.region = li.getRegion().adjust(2, 2, -2, -2);
39134         this.fireEvent("activate", this);
39135         return true;
39136     },
39137
39138     // private
39139     deactivate : function(){
39140         this.container.removeClass(this.activeClass);
39141         this.fireEvent("deactivate", this);
39142     },
39143
39144     // private
39145     shouldDeactivate : function(e){
39146         return !this.region || !this.region.contains(e.getPoint());
39147     },
39148
39149     // private
39150     handleClick : function(e){
39151         if(this.hideOnClick){
39152             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39153         }
39154     },
39155
39156     // private
39157     expandMenu : function(autoActivate){
39158         // do nothing
39159     },
39160
39161     // private
39162     hideMenu : function(){
39163         // do nothing
39164     }
39165 });/*
39166  * Based on:
39167  * Ext JS Library 1.1.1
39168  * Copyright(c) 2006-2007, Ext JS, LLC.
39169  *
39170  * Originally Released Under LGPL - original licence link has changed is not relivant.
39171  *
39172  * Fork - LGPL
39173  * <script type="text/javascript">
39174  */
39175  
39176 /**
39177  * @class Roo.menu.Adapter
39178  * @extends Roo.menu.BaseItem
39179  * @abstract
39180  * 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.
39181  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39182  * @constructor
39183  * Creates a new Adapter
39184  * @param {Object} config Configuration options
39185  */
39186 Roo.menu.Adapter = function(component, config){
39187     Roo.menu.Adapter.superclass.constructor.call(this, config);
39188     this.component = component;
39189 };
39190 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39191     // private
39192     canActivate : true,
39193
39194     // private
39195     onRender : function(container, position){
39196         this.component.render(container);
39197         this.el = this.component.getEl();
39198     },
39199
39200     // private
39201     activate : function(){
39202         if(this.disabled){
39203             return false;
39204         }
39205         this.component.focus();
39206         this.fireEvent("activate", this);
39207         return true;
39208     },
39209
39210     // private
39211     deactivate : function(){
39212         this.fireEvent("deactivate", this);
39213     },
39214
39215     // private
39216     disable : function(){
39217         this.component.disable();
39218         Roo.menu.Adapter.superclass.disable.call(this);
39219     },
39220
39221     // private
39222     enable : function(){
39223         this.component.enable();
39224         Roo.menu.Adapter.superclass.enable.call(this);
39225     }
39226 });/*
39227  * Based on:
39228  * Ext JS Library 1.1.1
39229  * Copyright(c) 2006-2007, Ext JS, LLC.
39230  *
39231  * Originally Released Under LGPL - original licence link has changed is not relivant.
39232  *
39233  * Fork - LGPL
39234  * <script type="text/javascript">
39235  */
39236
39237 /**
39238  * @class Roo.menu.TextItem
39239  * @extends Roo.menu.BaseItem
39240  * Adds a static text string to a menu, usually used as either a heading or group separator.
39241  * Note: old style constructor with text is still supported.
39242  * 
39243  * @constructor
39244  * Creates a new TextItem
39245  * @param {Object} cfg Configuration
39246  */
39247 Roo.menu.TextItem = function(cfg){
39248     if (typeof(cfg) == 'string') {
39249         this.text = cfg;
39250     } else {
39251         Roo.apply(this,cfg);
39252     }
39253     
39254     Roo.menu.TextItem.superclass.constructor.call(this);
39255 };
39256
39257 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39258     /**
39259      * @cfg {String} text Text to show on item.
39260      */
39261     text : '',
39262     
39263     /**
39264      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39265      */
39266     hideOnClick : false,
39267     /**
39268      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39269      */
39270     itemCls : "x-menu-text",
39271
39272     // private
39273     onRender : function(){
39274         var s = document.createElement("span");
39275         s.className = this.itemCls;
39276         s.innerHTML = this.text;
39277         this.el = s;
39278         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39279     }
39280 });/*
39281  * Based on:
39282  * Ext JS Library 1.1.1
39283  * Copyright(c) 2006-2007, Ext JS, LLC.
39284  *
39285  * Originally Released Under LGPL - original licence link has changed is not relivant.
39286  *
39287  * Fork - LGPL
39288  * <script type="text/javascript">
39289  */
39290
39291 /**
39292  * @class Roo.menu.Separator
39293  * @extends Roo.menu.BaseItem
39294  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39295  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39296  * @constructor
39297  * @param {Object} config Configuration options
39298  */
39299 Roo.menu.Separator = function(config){
39300     Roo.menu.Separator.superclass.constructor.call(this, config);
39301 };
39302
39303 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39304     /**
39305      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39306      */
39307     itemCls : "x-menu-sep",
39308     /**
39309      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39310      */
39311     hideOnClick : false,
39312
39313     // private
39314     onRender : function(li){
39315         var s = document.createElement("span");
39316         s.className = this.itemCls;
39317         s.innerHTML = "&#160;";
39318         this.el = s;
39319         li.addClass("x-menu-sep-li");
39320         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39321     }
39322 });/*
39323  * Based on:
39324  * Ext JS Library 1.1.1
39325  * Copyright(c) 2006-2007, Ext JS, LLC.
39326  *
39327  * Originally Released Under LGPL - original licence link has changed is not relivant.
39328  *
39329  * Fork - LGPL
39330  * <script type="text/javascript">
39331  */
39332 /**
39333  * @class Roo.menu.Item
39334  * @extends Roo.menu.BaseItem
39335  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39336  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39337  * activation and click handling.
39338  * @constructor
39339  * Creates a new Item
39340  * @param {Object} config Configuration options
39341  */
39342 Roo.menu.Item = function(config){
39343     Roo.menu.Item.superclass.constructor.call(this, config);
39344     if(this.menu){
39345         this.menu = Roo.menu.MenuMgr.get(this.menu);
39346     }
39347 };
39348 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39349     /**
39350      * @cfg {Roo.menu.Menu} menu
39351      * A Sub menu
39352      */
39353     /**
39354      * @cfg {String} text
39355      * The text to show on the menu item.
39356      */
39357     text: '',
39358      /**
39359      * @cfg {String} HTML to render in menu
39360      * The text to show on the menu item (HTML version).
39361      */
39362     html: '',
39363     /**
39364      * @cfg {String} icon
39365      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39366      */
39367     icon: undefined,
39368     /**
39369      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39370      */
39371     itemCls : "x-menu-item",
39372     /**
39373      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39374      */
39375     canActivate : true,
39376     /**
39377      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39378      */
39379     showDelay: 200,
39380     // doc'd in BaseItem
39381     hideDelay: 200,
39382
39383     // private
39384     ctype: "Roo.menu.Item",
39385     
39386     // private
39387     onRender : function(container, position){
39388         var el = document.createElement("a");
39389         el.hideFocus = true;
39390         el.unselectable = "on";
39391         el.href = this.href || "#";
39392         if(this.hrefTarget){
39393             el.target = this.hrefTarget;
39394         }
39395         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39396         
39397         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39398         
39399         el.innerHTML = String.format(
39400                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39401                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39402         this.el = el;
39403         Roo.menu.Item.superclass.onRender.call(this, container, position);
39404     },
39405
39406     /**
39407      * Sets the text to display in this menu item
39408      * @param {String} text The text to display
39409      * @param {Boolean} isHTML true to indicate text is pure html.
39410      */
39411     setText : function(text, isHTML){
39412         if (isHTML) {
39413             this.html = text;
39414         } else {
39415             this.text = text;
39416             this.html = '';
39417         }
39418         if(this.rendered){
39419             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39420      
39421             this.el.update(String.format(
39422                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39423                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39424             this.parentMenu.autoWidth();
39425         }
39426     },
39427
39428     // private
39429     handleClick : function(e){
39430         if(!this.href){ // if no link defined, stop the event automatically
39431             e.stopEvent();
39432         }
39433         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39434     },
39435
39436     // private
39437     activate : function(autoExpand){
39438         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39439             this.focus();
39440             if(autoExpand){
39441                 this.expandMenu();
39442             }
39443         }
39444         return true;
39445     },
39446
39447     // private
39448     shouldDeactivate : function(e){
39449         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39450             if(this.menu && this.menu.isVisible()){
39451                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39452             }
39453             return true;
39454         }
39455         return false;
39456     },
39457
39458     // private
39459     deactivate : function(){
39460         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39461         this.hideMenu();
39462     },
39463
39464     // private
39465     expandMenu : function(autoActivate){
39466         if(!this.disabled && this.menu){
39467             clearTimeout(this.hideTimer);
39468             delete this.hideTimer;
39469             if(!this.menu.isVisible() && !this.showTimer){
39470                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39471             }else if (this.menu.isVisible() && autoActivate){
39472                 this.menu.tryActivate(0, 1);
39473             }
39474         }
39475     },
39476
39477     // private
39478     deferExpand : function(autoActivate){
39479         delete this.showTimer;
39480         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39481         if(autoActivate){
39482             this.menu.tryActivate(0, 1);
39483         }
39484     },
39485
39486     // private
39487     hideMenu : function(){
39488         clearTimeout(this.showTimer);
39489         delete this.showTimer;
39490         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39491             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39492         }
39493     },
39494
39495     // private
39496     deferHide : function(){
39497         delete this.hideTimer;
39498         this.menu.hide();
39499     }
39500 });/*
39501  * Based on:
39502  * Ext JS Library 1.1.1
39503  * Copyright(c) 2006-2007, Ext JS, LLC.
39504  *
39505  * Originally Released Under LGPL - original licence link has changed is not relivant.
39506  *
39507  * Fork - LGPL
39508  * <script type="text/javascript">
39509  */
39510  
39511 /**
39512  * @class Roo.menu.CheckItem
39513  * @extends Roo.menu.Item
39514  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39515  * @constructor
39516  * Creates a new CheckItem
39517  * @param {Object} config Configuration options
39518  */
39519 Roo.menu.CheckItem = function(config){
39520     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39521     this.addEvents({
39522         /**
39523          * @event beforecheckchange
39524          * Fires before the checked value is set, providing an opportunity to cancel if needed
39525          * @param {Roo.menu.CheckItem} this
39526          * @param {Boolean} checked The new checked value that will be set
39527          */
39528         "beforecheckchange" : true,
39529         /**
39530          * @event checkchange
39531          * Fires after the checked value has been set
39532          * @param {Roo.menu.CheckItem} this
39533          * @param {Boolean} checked The checked value that was set
39534          */
39535         "checkchange" : true
39536     });
39537     if(this.checkHandler){
39538         this.on('checkchange', this.checkHandler, this.scope);
39539     }
39540 };
39541 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39542     /**
39543      * @cfg {String} group
39544      * All check items with the same group name will automatically be grouped into a single-select
39545      * radio button group (defaults to '')
39546      */
39547     /**
39548      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39549      */
39550     itemCls : "x-menu-item x-menu-check-item",
39551     /**
39552      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39553      */
39554     groupClass : "x-menu-group-item",
39555
39556     /**
39557      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39558      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39559      * initialized with checked = true will be rendered as checked.
39560      */
39561     checked: false,
39562
39563     // private
39564     ctype: "Roo.menu.CheckItem",
39565
39566     // private
39567     onRender : function(c){
39568         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39569         if(this.group){
39570             this.el.addClass(this.groupClass);
39571         }
39572         Roo.menu.MenuMgr.registerCheckable(this);
39573         if(this.checked){
39574             this.checked = false;
39575             this.setChecked(true, true);
39576         }
39577     },
39578
39579     // private
39580     destroy : function(){
39581         if(this.rendered){
39582             Roo.menu.MenuMgr.unregisterCheckable(this);
39583         }
39584         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39585     },
39586
39587     /**
39588      * Set the checked state of this item
39589      * @param {Boolean} checked The new checked value
39590      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39591      */
39592     setChecked : function(state, suppressEvent){
39593         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39594             if(this.container){
39595                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39596             }
39597             this.checked = state;
39598             if(suppressEvent !== true){
39599                 this.fireEvent("checkchange", this, state);
39600             }
39601         }
39602     },
39603
39604     // private
39605     handleClick : function(e){
39606        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39607            this.setChecked(!this.checked);
39608        }
39609        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39610     }
39611 });/*
39612  * Based on:
39613  * Ext JS Library 1.1.1
39614  * Copyright(c) 2006-2007, Ext JS, LLC.
39615  *
39616  * Originally Released Under LGPL - original licence link has changed is not relivant.
39617  *
39618  * Fork - LGPL
39619  * <script type="text/javascript">
39620  */
39621  
39622 /**
39623  * @class Roo.menu.DateItem
39624  * @extends Roo.menu.Adapter
39625  * A menu item that wraps the {@link Roo.DatPicker} component.
39626  * @constructor
39627  * Creates a new DateItem
39628  * @param {Object} config Configuration options
39629  */
39630 Roo.menu.DateItem = function(config){
39631     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39632     /** The Roo.DatePicker object @type Roo.DatePicker */
39633     this.picker = this.component;
39634     this.addEvents({select: true});
39635     
39636     this.picker.on("render", function(picker){
39637         picker.getEl().swallowEvent("click");
39638         picker.container.addClass("x-menu-date-item");
39639     });
39640
39641     this.picker.on("select", this.onSelect, this);
39642 };
39643
39644 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39645     // private
39646     onSelect : function(picker, date){
39647         this.fireEvent("select", this, date, picker);
39648         Roo.menu.DateItem.superclass.handleClick.call(this);
39649     }
39650 });/*
39651  * Based on:
39652  * Ext JS Library 1.1.1
39653  * Copyright(c) 2006-2007, Ext JS, LLC.
39654  *
39655  * Originally Released Under LGPL - original licence link has changed is not relivant.
39656  *
39657  * Fork - LGPL
39658  * <script type="text/javascript">
39659  */
39660  
39661 /**
39662  * @class Roo.menu.ColorItem
39663  * @extends Roo.menu.Adapter
39664  * A menu item that wraps the {@link Roo.ColorPalette} component.
39665  * @constructor
39666  * Creates a new ColorItem
39667  * @param {Object} config Configuration options
39668  */
39669 Roo.menu.ColorItem = function(config){
39670     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39671     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39672     this.palette = this.component;
39673     this.relayEvents(this.palette, ["select"]);
39674     if(this.selectHandler){
39675         this.on('select', this.selectHandler, this.scope);
39676     }
39677 };
39678 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39679  * Based on:
39680  * Ext JS Library 1.1.1
39681  * Copyright(c) 2006-2007, Ext JS, LLC.
39682  *
39683  * Originally Released Under LGPL - original licence link has changed is not relivant.
39684  *
39685  * Fork - LGPL
39686  * <script type="text/javascript">
39687  */
39688  
39689
39690 /**
39691  * @class Roo.menu.DateMenu
39692  * @extends Roo.menu.Menu
39693  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39694  * @constructor
39695  * Creates a new DateMenu
39696  * @param {Object} config Configuration options
39697  */
39698 Roo.menu.DateMenu = function(config){
39699     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39700     this.plain = true;
39701     var di = new Roo.menu.DateItem(config);
39702     this.add(di);
39703     /**
39704      * The {@link Roo.DatePicker} instance for this DateMenu
39705      * @type DatePicker
39706      */
39707     this.picker = di.picker;
39708     /**
39709      * @event select
39710      * @param {DatePicker} picker
39711      * @param {Date} date
39712      */
39713     this.relayEvents(di, ["select"]);
39714     this.on('beforeshow', function(){
39715         if(this.picker){
39716             this.picker.hideMonthPicker(false);
39717         }
39718     }, this);
39719 };
39720 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39721     cls:'x-date-menu'
39722 });/*
39723  * Based on:
39724  * Ext JS Library 1.1.1
39725  * Copyright(c) 2006-2007, Ext JS, LLC.
39726  *
39727  * Originally Released Under LGPL - original licence link has changed is not relivant.
39728  *
39729  * Fork - LGPL
39730  * <script type="text/javascript">
39731  */
39732  
39733
39734 /**
39735  * @class Roo.menu.ColorMenu
39736  * @extends Roo.menu.Menu
39737  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39738  * @constructor
39739  * Creates a new ColorMenu
39740  * @param {Object} config Configuration options
39741  */
39742 Roo.menu.ColorMenu = function(config){
39743     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39744     this.plain = true;
39745     var ci = new Roo.menu.ColorItem(config);
39746     this.add(ci);
39747     /**
39748      * The {@link Roo.ColorPalette} instance for this ColorMenu
39749      * @type ColorPalette
39750      */
39751     this.palette = ci.palette;
39752     /**
39753      * @event select
39754      * @param {ColorPalette} palette
39755      * @param {String} color
39756      */
39757     this.relayEvents(ci, ["select"]);
39758 };
39759 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39760  * Based on:
39761  * Ext JS Library 1.1.1
39762  * Copyright(c) 2006-2007, Ext JS, LLC.
39763  *
39764  * Originally Released Under LGPL - original licence link has changed is not relivant.
39765  *
39766  * Fork - LGPL
39767  * <script type="text/javascript">
39768  */
39769  
39770 /**
39771  * @class Roo.form.TextItem
39772  * @extends Roo.BoxComponent
39773  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39774  * @constructor
39775  * Creates a new TextItem
39776  * @param {Object} config Configuration options
39777  */
39778 Roo.form.TextItem = function(config){
39779     Roo.form.TextItem.superclass.constructor.call(this, config);
39780 };
39781
39782 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39783     
39784     /**
39785      * @cfg {String} tag the tag for this item (default div)
39786      */
39787     tag : 'div',
39788     /**
39789      * @cfg {String} html the content for this item
39790      */
39791     html : '',
39792     
39793     getAutoCreate : function()
39794     {
39795         var cfg = {
39796             id: this.id,
39797             tag: this.tag,
39798             html: this.html,
39799             cls: 'x-form-item'
39800         };
39801         
39802         return cfg;
39803         
39804     },
39805     
39806     onRender : function(ct, position)
39807     {
39808         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39809         
39810         if(!this.el){
39811             var cfg = this.getAutoCreate();
39812             if(!cfg.name){
39813                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39814             }
39815             if (!cfg.name.length) {
39816                 delete cfg.name;
39817             }
39818             this.el = ct.createChild(cfg, position);
39819         }
39820     },
39821     /*
39822      * setHTML
39823      * @param {String} html update the Contents of the element.
39824      */
39825     setHTML : function(html)
39826     {
39827         this.fieldEl.dom.innerHTML = html;
39828     }
39829     
39830 });/*
39831  * Based on:
39832  * Ext JS Library 1.1.1
39833  * Copyright(c) 2006-2007, Ext JS, LLC.
39834  *
39835  * Originally Released Under LGPL - original licence link has changed is not relivant.
39836  *
39837  * Fork - LGPL
39838  * <script type="text/javascript">
39839  */
39840  
39841 /**
39842  * @class Roo.form.Field
39843  * @extends Roo.BoxComponent
39844  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39845  * @constructor
39846  * Creates a new Field
39847  * @param {Object} config Configuration options
39848  */
39849 Roo.form.Field = function(config){
39850     Roo.form.Field.superclass.constructor.call(this, config);
39851 };
39852
39853 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39854     /**
39855      * @cfg {String} fieldLabel Label to use when rendering a form.
39856      */
39857        /**
39858      * @cfg {String} qtip Mouse over tip
39859      */
39860      
39861     /**
39862      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39863      */
39864     invalidClass : "x-form-invalid",
39865     /**
39866      * @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")
39867      */
39868     invalidText : "The value in this field is invalid",
39869     /**
39870      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39871      */
39872     focusClass : "x-form-focus",
39873     /**
39874      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39875       automatic validation (defaults to "keyup").
39876      */
39877     validationEvent : "keyup",
39878     /**
39879      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39880      */
39881     validateOnBlur : true,
39882     /**
39883      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39884      */
39885     validationDelay : 250,
39886     /**
39887      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39888      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39889      */
39890     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39891     /**
39892      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39893      */
39894     fieldClass : "x-form-field",
39895     /**
39896      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39897      *<pre>
39898 Value         Description
39899 -----------   ----------------------------------------------------------------------
39900 qtip          Display a quick tip when the user hovers over the field
39901 title         Display a default browser title attribute popup
39902 under         Add a block div beneath the field containing the error text
39903 side          Add an error icon to the right of the field with a popup on hover
39904 [element id]  Add the error text directly to the innerHTML of the specified element
39905 </pre>
39906      */
39907     msgTarget : 'qtip',
39908     /**
39909      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39910      */
39911     msgFx : 'normal',
39912
39913     /**
39914      * @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.
39915      */
39916     readOnly : false,
39917
39918     /**
39919      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39920      */
39921     disabled : false,
39922
39923     /**
39924      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39925      */
39926     inputType : undefined,
39927     
39928     /**
39929      * @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).
39930          */
39931         tabIndex : undefined,
39932         
39933     // private
39934     isFormField : true,
39935
39936     // private
39937     hasFocus : false,
39938     /**
39939      * @property {Roo.Element} fieldEl
39940      * Element Containing the rendered Field (with label etc.)
39941      */
39942     /**
39943      * @cfg {Mixed} value A value to initialize this field with.
39944      */
39945     value : undefined,
39946
39947     /**
39948      * @cfg {String} name The field's HTML name attribute.
39949      */
39950     /**
39951      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39952      */
39953     // private
39954     loadedValue : false,
39955      
39956      
39957         // private ??
39958         initComponent : function(){
39959         Roo.form.Field.superclass.initComponent.call(this);
39960         this.addEvents({
39961             /**
39962              * @event focus
39963              * Fires when this field receives input focus.
39964              * @param {Roo.form.Field} this
39965              */
39966             focus : true,
39967             /**
39968              * @event blur
39969              * Fires when this field loses input focus.
39970              * @param {Roo.form.Field} this
39971              */
39972             blur : true,
39973             /**
39974              * @event specialkey
39975              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39976              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39977              * @param {Roo.form.Field} this
39978              * @param {Roo.EventObject} e The event object
39979              */
39980             specialkey : true,
39981             /**
39982              * @event change
39983              * Fires just before the field blurs if the field value has changed.
39984              * @param {Roo.form.Field} this
39985              * @param {Mixed} newValue The new value
39986              * @param {Mixed} oldValue The original value
39987              */
39988             change : true,
39989             /**
39990              * @event invalid
39991              * Fires after the field has been marked as invalid.
39992              * @param {Roo.form.Field} this
39993              * @param {String} msg The validation message
39994              */
39995             invalid : true,
39996             /**
39997              * @event valid
39998              * Fires after the field has been validated with no errors.
39999              * @param {Roo.form.Field} this
40000              */
40001             valid : true,
40002              /**
40003              * @event keyup
40004              * Fires after the key up
40005              * @param {Roo.form.Field} this
40006              * @param {Roo.EventObject}  e The event Object
40007              */
40008             keyup : true
40009         });
40010     },
40011
40012     /**
40013      * Returns the name attribute of the field if available
40014      * @return {String} name The field name
40015      */
40016     getName: function(){
40017          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40018     },
40019
40020     // private
40021     onRender : function(ct, position){
40022         Roo.form.Field.superclass.onRender.call(this, ct, position);
40023         if(!this.el){
40024             var cfg = this.getAutoCreate();
40025             if(!cfg.name){
40026                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40027             }
40028             if (!cfg.name.length) {
40029                 delete cfg.name;
40030             }
40031             if(this.inputType){
40032                 cfg.type = this.inputType;
40033             }
40034             this.el = ct.createChild(cfg, position);
40035         }
40036         var type = this.el.dom.type;
40037         if(type){
40038             if(type == 'password'){
40039                 type = 'text';
40040             }
40041             this.el.addClass('x-form-'+type);
40042         }
40043         if(this.readOnly){
40044             this.el.dom.readOnly = true;
40045         }
40046         if(this.tabIndex !== undefined){
40047             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40048         }
40049
40050         this.el.addClass([this.fieldClass, this.cls]);
40051         this.initValue();
40052     },
40053
40054     /**
40055      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40056      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40057      * @return {Roo.form.Field} this
40058      */
40059     applyTo : function(target){
40060         this.allowDomMove = false;
40061         this.el = Roo.get(target);
40062         this.render(this.el.dom.parentNode);
40063         return this;
40064     },
40065
40066     // private
40067     initValue : function(){
40068         if(this.value !== undefined){
40069             this.setValue(this.value);
40070         }else if(this.el.dom.value.length > 0){
40071             this.setValue(this.el.dom.value);
40072         }
40073     },
40074
40075     /**
40076      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40077      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40078      */
40079     isDirty : function() {
40080         if(this.disabled) {
40081             return false;
40082         }
40083         return String(this.getValue()) !== String(this.originalValue);
40084     },
40085
40086     /**
40087      * stores the current value in loadedValue
40088      */
40089     resetHasChanged : function()
40090     {
40091         this.loadedValue = String(this.getValue());
40092     },
40093     /**
40094      * checks the current value against the 'loaded' value.
40095      * Note - will return false if 'resetHasChanged' has not been called first.
40096      */
40097     hasChanged : function()
40098     {
40099         if(this.disabled || this.readOnly) {
40100             return false;
40101         }
40102         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40103     },
40104     
40105     
40106     
40107     // private
40108     afterRender : function(){
40109         Roo.form.Field.superclass.afterRender.call(this);
40110         this.initEvents();
40111     },
40112
40113     // private
40114     fireKey : function(e){
40115         //Roo.log('field ' + e.getKey());
40116         if(e.isNavKeyPress()){
40117             this.fireEvent("specialkey", this, e);
40118         }
40119     },
40120
40121     /**
40122      * Resets the current field value to the originally loaded value and clears any validation messages
40123      */
40124     reset : function(){
40125         this.setValue(this.resetValue);
40126         this.originalValue = this.getValue();
40127         this.clearInvalid();
40128     },
40129
40130     // private
40131     initEvents : function(){
40132         // safari killled keypress - so keydown is now used..
40133         this.el.on("keydown" , this.fireKey,  this);
40134         this.el.on("focus", this.onFocus,  this);
40135         this.el.on("blur", this.onBlur,  this);
40136         this.el.relayEvent('keyup', this);
40137
40138         // reference to original value for reset
40139         this.originalValue = this.getValue();
40140         this.resetValue =  this.getValue();
40141     },
40142
40143     // private
40144     onFocus : function(){
40145         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40146             this.el.addClass(this.focusClass);
40147         }
40148         if(!this.hasFocus){
40149             this.hasFocus = true;
40150             this.startValue = this.getValue();
40151             this.fireEvent("focus", this);
40152         }
40153     },
40154
40155     beforeBlur : Roo.emptyFn,
40156
40157     // private
40158     onBlur : function(){
40159         this.beforeBlur();
40160         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40161             this.el.removeClass(this.focusClass);
40162         }
40163         this.hasFocus = false;
40164         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40165             this.validate();
40166         }
40167         var v = this.getValue();
40168         if(String(v) !== String(this.startValue)){
40169             this.fireEvent('change', this, v, this.startValue);
40170         }
40171         this.fireEvent("blur", this);
40172     },
40173
40174     /**
40175      * Returns whether or not the field value is currently valid
40176      * @param {Boolean} preventMark True to disable marking the field invalid
40177      * @return {Boolean} True if the value is valid, else false
40178      */
40179     isValid : function(preventMark){
40180         if(this.disabled){
40181             return true;
40182         }
40183         var restore = this.preventMark;
40184         this.preventMark = preventMark === true;
40185         var v = this.validateValue(this.processValue(this.getRawValue()));
40186         this.preventMark = restore;
40187         return v;
40188     },
40189
40190     /**
40191      * Validates the field value
40192      * @return {Boolean} True if the value is valid, else false
40193      */
40194     validate : function(){
40195         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40196             this.clearInvalid();
40197             return true;
40198         }
40199         return false;
40200     },
40201
40202     processValue : function(value){
40203         return value;
40204     },
40205
40206     // private
40207     // Subclasses should provide the validation implementation by overriding this
40208     validateValue : function(value){
40209         return true;
40210     },
40211
40212     /**
40213      * Mark this field as invalid
40214      * @param {String} msg The validation message
40215      */
40216     markInvalid : function(msg){
40217         if(!this.rendered || this.preventMark){ // not rendered
40218             return;
40219         }
40220         
40221         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40222         
40223         obj.el.addClass(this.invalidClass);
40224         msg = msg || this.invalidText;
40225         switch(this.msgTarget){
40226             case 'qtip':
40227                 obj.el.dom.qtip = msg;
40228                 obj.el.dom.qclass = 'x-form-invalid-tip';
40229                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40230                     Roo.QuickTips.enable();
40231                 }
40232                 break;
40233             case 'title':
40234                 this.el.dom.title = msg;
40235                 break;
40236             case 'under':
40237                 if(!this.errorEl){
40238                     var elp = this.el.findParent('.x-form-element', 5, true);
40239                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40240                     this.errorEl.setWidth(elp.getWidth(true)-20);
40241                 }
40242                 this.errorEl.update(msg);
40243                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40244                 break;
40245             case 'side':
40246                 if(!this.errorIcon){
40247                     var elp = this.el.findParent('.x-form-element', 5, true);
40248                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40249                 }
40250                 this.alignErrorIcon();
40251                 this.errorIcon.dom.qtip = msg;
40252                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40253                 this.errorIcon.show();
40254                 this.on('resize', this.alignErrorIcon, this);
40255                 break;
40256             default:
40257                 var t = Roo.getDom(this.msgTarget);
40258                 t.innerHTML = msg;
40259                 t.style.display = this.msgDisplay;
40260                 break;
40261         }
40262         this.fireEvent('invalid', this, msg);
40263     },
40264
40265     // private
40266     alignErrorIcon : function(){
40267         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40268     },
40269
40270     /**
40271      * Clear any invalid styles/messages for this field
40272      */
40273     clearInvalid : function(){
40274         if(!this.rendered || this.preventMark){ // not rendered
40275             return;
40276         }
40277         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40278         
40279         obj.el.removeClass(this.invalidClass);
40280         switch(this.msgTarget){
40281             case 'qtip':
40282                 obj.el.dom.qtip = '';
40283                 break;
40284             case 'title':
40285                 this.el.dom.title = '';
40286                 break;
40287             case 'under':
40288                 if(this.errorEl){
40289                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40290                 }
40291                 break;
40292             case 'side':
40293                 if(this.errorIcon){
40294                     this.errorIcon.dom.qtip = '';
40295                     this.errorIcon.hide();
40296                     this.un('resize', this.alignErrorIcon, this);
40297                 }
40298                 break;
40299             default:
40300                 var t = Roo.getDom(this.msgTarget);
40301                 t.innerHTML = '';
40302                 t.style.display = 'none';
40303                 break;
40304         }
40305         this.fireEvent('valid', this);
40306     },
40307
40308     /**
40309      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40310      * @return {Mixed} value The field value
40311      */
40312     getRawValue : function(){
40313         var v = this.el.getValue();
40314         
40315         return v;
40316     },
40317
40318     /**
40319      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40320      * @return {Mixed} value The field value
40321      */
40322     getValue : function(){
40323         var v = this.el.getValue();
40324          
40325         return v;
40326     },
40327
40328     /**
40329      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40330      * @param {Mixed} value The value to set
40331      */
40332     setRawValue : function(v){
40333         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40334     },
40335
40336     /**
40337      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40338      * @param {Mixed} value The value to set
40339      */
40340     setValue : function(v){
40341         this.value = v;
40342         if(this.rendered){
40343             this.el.dom.value = (v === null || v === undefined ? '' : v);
40344              this.validate();
40345         }
40346     },
40347
40348     adjustSize : function(w, h){
40349         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40350         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40351         return s;
40352     },
40353
40354     adjustWidth : function(tag, w){
40355         tag = tag.toLowerCase();
40356         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40357             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40358                 if(tag == 'input'){
40359                     return w + 2;
40360                 }
40361                 if(tag == 'textarea'){
40362                     return w-2;
40363                 }
40364             }else if(Roo.isOpera){
40365                 if(tag == 'input'){
40366                     return w + 2;
40367                 }
40368                 if(tag == 'textarea'){
40369                     return w-2;
40370                 }
40371             }
40372         }
40373         return w;
40374     }
40375 });
40376
40377
40378 // anything other than normal should be considered experimental
40379 Roo.form.Field.msgFx = {
40380     normal : {
40381         show: function(msgEl, f){
40382             msgEl.setDisplayed('block');
40383         },
40384
40385         hide : function(msgEl, f){
40386             msgEl.setDisplayed(false).update('');
40387         }
40388     },
40389
40390     slide : {
40391         show: function(msgEl, f){
40392             msgEl.slideIn('t', {stopFx:true});
40393         },
40394
40395         hide : function(msgEl, f){
40396             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40397         }
40398     },
40399
40400     slideRight : {
40401         show: function(msgEl, f){
40402             msgEl.fixDisplay();
40403             msgEl.alignTo(f.el, 'tl-tr');
40404             msgEl.slideIn('l', {stopFx:true});
40405         },
40406
40407         hide : function(msgEl, f){
40408             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40409         }
40410     }
40411 };/*
40412  * Based on:
40413  * Ext JS Library 1.1.1
40414  * Copyright(c) 2006-2007, Ext JS, LLC.
40415  *
40416  * Originally Released Under LGPL - original licence link has changed is not relivant.
40417  *
40418  * Fork - LGPL
40419  * <script type="text/javascript">
40420  */
40421  
40422
40423 /**
40424  * @class Roo.form.TextField
40425  * @extends Roo.form.Field
40426  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40427  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40428  * @constructor
40429  * Creates a new TextField
40430  * @param {Object} config Configuration options
40431  */
40432 Roo.form.TextField = function(config){
40433     Roo.form.TextField.superclass.constructor.call(this, config);
40434     this.addEvents({
40435         /**
40436          * @event autosize
40437          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40438          * according to the default logic, but this event provides a hook for the developer to apply additional
40439          * logic at runtime to resize the field if needed.
40440              * @param {Roo.form.Field} this This text field
40441              * @param {Number} width The new field width
40442              */
40443         autosize : true
40444     });
40445 };
40446
40447 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40448     /**
40449      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40450      */
40451     grow : false,
40452     /**
40453      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40454      */
40455     growMin : 30,
40456     /**
40457      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40458      */
40459     growMax : 800,
40460     /**
40461      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40462      */
40463     vtype : null,
40464     /**
40465      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40466      */
40467     maskRe : null,
40468     /**
40469      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40470      */
40471     disableKeyFilter : false,
40472     /**
40473      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40474      */
40475     allowBlank : true,
40476     /**
40477      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40478      */
40479     minLength : 0,
40480     /**
40481      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40482      */
40483     maxLength : Number.MAX_VALUE,
40484     /**
40485      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40486      */
40487     minLengthText : "The minimum length for this field is {0}",
40488     /**
40489      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40490      */
40491     maxLengthText : "The maximum length for this field is {0}",
40492     /**
40493      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40494      */
40495     selectOnFocus : false,
40496     /**
40497      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40498      */    
40499     allowLeadingSpace : false,
40500     /**
40501      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40502      */
40503     blankText : "This field is required",
40504     /**
40505      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40506      * If available, this function will be called only after the basic validators all return true, and will be passed the
40507      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40508      */
40509     validator : null,
40510     /**
40511      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40512      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40513      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40514      */
40515     regex : null,
40516     /**
40517      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40518      */
40519     regexText : "",
40520     /**
40521      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40522      */
40523     emptyText : null,
40524    
40525
40526     // private
40527     initEvents : function()
40528     {
40529         if (this.emptyText) {
40530             this.el.attr('placeholder', this.emptyText);
40531         }
40532         
40533         Roo.form.TextField.superclass.initEvents.call(this);
40534         if(this.validationEvent == 'keyup'){
40535             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40536             this.el.on('keyup', this.filterValidation, this);
40537         }
40538         else if(this.validationEvent !== false){
40539             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40540         }
40541         
40542         if(this.selectOnFocus){
40543             this.on("focus", this.preFocus, this);
40544         }
40545         if (!this.allowLeadingSpace) {
40546             this.on('blur', this.cleanLeadingSpace, this);
40547         }
40548         
40549         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40550             this.el.on("keypress", this.filterKeys, this);
40551         }
40552         if(this.grow){
40553             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40554             this.el.on("click", this.autoSize,  this);
40555         }
40556         if(this.el.is('input[type=password]') && Roo.isSafari){
40557             this.el.on('keydown', this.SafariOnKeyDown, this);
40558         }
40559     },
40560
40561     processValue : function(value){
40562         if(this.stripCharsRe){
40563             var newValue = value.replace(this.stripCharsRe, '');
40564             if(newValue !== value){
40565                 this.setRawValue(newValue);
40566                 return newValue;
40567             }
40568         }
40569         return value;
40570     },
40571
40572     filterValidation : function(e){
40573         if(!e.isNavKeyPress()){
40574             this.validationTask.delay(this.validationDelay);
40575         }
40576     },
40577
40578     // private
40579     onKeyUp : function(e){
40580         if(!e.isNavKeyPress()){
40581             this.autoSize();
40582         }
40583     },
40584     // private - clean the leading white space
40585     cleanLeadingSpace : function(e)
40586     {
40587         if ( this.inputType == 'file') {
40588             return;
40589         }
40590         
40591         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40592     },
40593     /**
40594      * Resets the current field value to the originally-loaded value and clears any validation messages.
40595      *  
40596      */
40597     reset : function(){
40598         Roo.form.TextField.superclass.reset.call(this);
40599        
40600     }, 
40601     // private
40602     preFocus : function(){
40603         
40604         if(this.selectOnFocus){
40605             this.el.dom.select();
40606         }
40607     },
40608
40609     
40610     // private
40611     filterKeys : function(e){
40612         var k = e.getKey();
40613         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40614             return;
40615         }
40616         var c = e.getCharCode(), cc = String.fromCharCode(c);
40617         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40618             return;
40619         }
40620         if(!this.maskRe.test(cc)){
40621             e.stopEvent();
40622         }
40623     },
40624
40625     setValue : function(v){
40626         
40627         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40628         
40629         this.autoSize();
40630     },
40631
40632     /**
40633      * Validates a value according to the field's validation rules and marks the field as invalid
40634      * if the validation fails
40635      * @param {Mixed} value The value to validate
40636      * @return {Boolean} True if the value is valid, else false
40637      */
40638     validateValue : function(value){
40639         if(value.length < 1)  { // if it's blank
40640              if(this.allowBlank){
40641                 this.clearInvalid();
40642                 return true;
40643              }else{
40644                 this.markInvalid(this.blankText);
40645                 return false;
40646              }
40647         }
40648         if(value.length < this.minLength){
40649             this.markInvalid(String.format(this.minLengthText, this.minLength));
40650             return false;
40651         }
40652         if(value.length > this.maxLength){
40653             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40654             return false;
40655         }
40656         if(this.vtype){
40657             var vt = Roo.form.VTypes;
40658             if(!vt[this.vtype](value, this)){
40659                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40660                 return false;
40661             }
40662         }
40663         if(typeof this.validator == "function"){
40664             var msg = this.validator(value);
40665             if(msg !== true){
40666                 this.markInvalid(msg);
40667                 return false;
40668             }
40669         }
40670         if(this.regex && !this.regex.test(value)){
40671             this.markInvalid(this.regexText);
40672             return false;
40673         }
40674         return true;
40675     },
40676
40677     /**
40678      * Selects text in this field
40679      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40680      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40681      */
40682     selectText : function(start, end){
40683         var v = this.getRawValue();
40684         if(v.length > 0){
40685             start = start === undefined ? 0 : start;
40686             end = end === undefined ? v.length : end;
40687             var d = this.el.dom;
40688             if(d.setSelectionRange){
40689                 d.setSelectionRange(start, end);
40690             }else if(d.createTextRange){
40691                 var range = d.createTextRange();
40692                 range.moveStart("character", start);
40693                 range.moveEnd("character", v.length-end);
40694                 range.select();
40695             }
40696         }
40697     },
40698
40699     /**
40700      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40701      * This only takes effect if grow = true, and fires the autosize event.
40702      */
40703     autoSize : function(){
40704         if(!this.grow || !this.rendered){
40705             return;
40706         }
40707         if(!this.metrics){
40708             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40709         }
40710         var el = this.el;
40711         var v = el.dom.value;
40712         var d = document.createElement('div');
40713         d.appendChild(document.createTextNode(v));
40714         v = d.innerHTML;
40715         d = null;
40716         v += "&#160;";
40717         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40718         this.el.setWidth(w);
40719         this.fireEvent("autosize", this, w);
40720     },
40721     
40722     // private
40723     SafariOnKeyDown : function(event)
40724     {
40725         // this is a workaround for a password hang bug on chrome/ webkit.
40726         
40727         var isSelectAll = false;
40728         
40729         if(this.el.dom.selectionEnd > 0){
40730             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40731         }
40732         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40733             event.preventDefault();
40734             this.setValue('');
40735             return;
40736         }
40737         
40738         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40739             
40740             event.preventDefault();
40741             // this is very hacky as keydown always get's upper case.
40742             
40743             var cc = String.fromCharCode(event.getCharCode());
40744             
40745             
40746             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40747             
40748         }
40749         
40750         
40751     }
40752 });/*
40753  * Based on:
40754  * Ext JS Library 1.1.1
40755  * Copyright(c) 2006-2007, Ext JS, LLC.
40756  *
40757  * Originally Released Under LGPL - original licence link has changed is not relivant.
40758  *
40759  * Fork - LGPL
40760  * <script type="text/javascript">
40761  */
40762  
40763 /**
40764  * @class Roo.form.Hidden
40765  * @extends Roo.form.TextField
40766  * Simple Hidden element used on forms 
40767  * 
40768  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40769  * 
40770  * @constructor
40771  * Creates a new Hidden form element.
40772  * @param {Object} config Configuration options
40773  */
40774
40775
40776
40777 // easy hidden field...
40778 Roo.form.Hidden = function(config){
40779     Roo.form.Hidden.superclass.constructor.call(this, config);
40780 };
40781   
40782 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40783     fieldLabel:      '',
40784     inputType:      'hidden',
40785     width:          50,
40786     allowBlank:     true,
40787     labelSeparator: '',
40788     hidden:         true,
40789     itemCls :       'x-form-item-display-none'
40790
40791
40792 });
40793
40794
40795 /*
40796  * Based on:
40797  * Ext JS Library 1.1.1
40798  * Copyright(c) 2006-2007, Ext JS, LLC.
40799  *
40800  * Originally Released Under LGPL - original licence link has changed is not relivant.
40801  *
40802  * Fork - LGPL
40803  * <script type="text/javascript">
40804  */
40805  
40806 /**
40807  * @class Roo.form.TriggerField
40808  * @extends Roo.form.TextField
40809  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40810  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40811  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40812  * for which you can provide a custom implementation.  For example:
40813  * <pre><code>
40814 var trigger = new Roo.form.TriggerField();
40815 trigger.onTriggerClick = myTriggerFn;
40816 trigger.applyTo('my-field');
40817 </code></pre>
40818  *
40819  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40820  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40821  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40822  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40823  * @constructor
40824  * Create a new TriggerField.
40825  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40826  * to the base TextField)
40827  */
40828 Roo.form.TriggerField = function(config){
40829     this.mimicing = false;
40830     Roo.form.TriggerField.superclass.constructor.call(this, config);
40831 };
40832
40833 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40834     /**
40835      * @cfg {String} triggerClass A CSS class to apply to the trigger
40836      */
40837     /**
40838      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40839      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40840      */
40841     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40842     /**
40843      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40844      */
40845     hideTrigger:false,
40846
40847     /** @cfg {Boolean} grow @hide */
40848     /** @cfg {Number} growMin @hide */
40849     /** @cfg {Number} growMax @hide */
40850
40851     /**
40852      * @hide 
40853      * @method
40854      */
40855     autoSize: Roo.emptyFn,
40856     // private
40857     monitorTab : true,
40858     // private
40859     deferHeight : true,
40860
40861     
40862     actionMode : 'wrap',
40863     // private
40864     onResize : function(w, h){
40865         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40866         if(typeof w == 'number'){
40867             var x = w - this.trigger.getWidth();
40868             this.el.setWidth(this.adjustWidth('input', x));
40869             this.trigger.setStyle('left', x+'px');
40870         }
40871     },
40872
40873     // private
40874     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40875
40876     // private
40877     getResizeEl : function(){
40878         return this.wrap;
40879     },
40880
40881     // private
40882     getPositionEl : function(){
40883         return this.wrap;
40884     },
40885
40886     // private
40887     alignErrorIcon : function(){
40888         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40889     },
40890
40891     // private
40892     onRender : function(ct, position){
40893         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40894         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40895         this.trigger = this.wrap.createChild(this.triggerConfig ||
40896                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40897         if(this.hideTrigger){
40898             this.trigger.setDisplayed(false);
40899         }
40900         this.initTrigger();
40901         if(!this.width){
40902             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40903         }
40904     },
40905
40906     // private
40907     initTrigger : function(){
40908         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40909         this.trigger.addClassOnOver('x-form-trigger-over');
40910         this.trigger.addClassOnClick('x-form-trigger-click');
40911     },
40912
40913     // private
40914     onDestroy : function(){
40915         if(this.trigger){
40916             this.trigger.removeAllListeners();
40917             this.trigger.remove();
40918         }
40919         if(this.wrap){
40920             this.wrap.remove();
40921         }
40922         Roo.form.TriggerField.superclass.onDestroy.call(this);
40923     },
40924
40925     // private
40926     onFocus : function(){
40927         Roo.form.TriggerField.superclass.onFocus.call(this);
40928         if(!this.mimicing){
40929             this.wrap.addClass('x-trigger-wrap-focus');
40930             this.mimicing = true;
40931             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40932             if(this.monitorTab){
40933                 this.el.on("keydown", this.checkTab, this);
40934             }
40935         }
40936     },
40937
40938     // private
40939     checkTab : function(e){
40940         if(e.getKey() == e.TAB){
40941             this.triggerBlur();
40942         }
40943     },
40944
40945     // private
40946     onBlur : function(){
40947         // do nothing
40948     },
40949
40950     // private
40951     mimicBlur : function(e, t){
40952         if(!this.wrap.contains(t) && this.validateBlur()){
40953             this.triggerBlur();
40954         }
40955     },
40956
40957     // private
40958     triggerBlur : function(){
40959         this.mimicing = false;
40960         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40961         if(this.monitorTab){
40962             this.el.un("keydown", this.checkTab, this);
40963         }
40964         this.wrap.removeClass('x-trigger-wrap-focus');
40965         Roo.form.TriggerField.superclass.onBlur.call(this);
40966     },
40967
40968     // private
40969     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40970     validateBlur : function(e, t){
40971         return true;
40972     },
40973
40974     // private
40975     onDisable : function(){
40976         Roo.form.TriggerField.superclass.onDisable.call(this);
40977         if(this.wrap){
40978             this.wrap.addClass('x-item-disabled');
40979         }
40980     },
40981
40982     // private
40983     onEnable : function(){
40984         Roo.form.TriggerField.superclass.onEnable.call(this);
40985         if(this.wrap){
40986             this.wrap.removeClass('x-item-disabled');
40987         }
40988     },
40989
40990     // private
40991     onShow : function(){
40992         var ae = this.getActionEl();
40993         
40994         if(ae){
40995             ae.dom.style.display = '';
40996             ae.dom.style.visibility = 'visible';
40997         }
40998     },
40999
41000     // private
41001     
41002     onHide : function(){
41003         var ae = this.getActionEl();
41004         ae.dom.style.display = 'none';
41005     },
41006
41007     /**
41008      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41009      * by an implementing function.
41010      * @method
41011      * @param {EventObject} e
41012      */
41013     onTriggerClick : Roo.emptyFn
41014 });
41015
41016 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41017 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41018 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41019 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41020     initComponent : function(){
41021         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41022
41023         this.triggerConfig = {
41024             tag:'span', cls:'x-form-twin-triggers', cn:[
41025             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41026             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41027         ]};
41028     },
41029
41030     getTrigger : function(index){
41031         return this.triggers[index];
41032     },
41033
41034     initTrigger : function(){
41035         var ts = this.trigger.select('.x-form-trigger', true);
41036         this.wrap.setStyle('overflow', 'hidden');
41037         var triggerField = this;
41038         ts.each(function(t, all, index){
41039             t.hide = function(){
41040                 var w = triggerField.wrap.getWidth();
41041                 this.dom.style.display = 'none';
41042                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41043             };
41044             t.show = function(){
41045                 var w = triggerField.wrap.getWidth();
41046                 this.dom.style.display = '';
41047                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41048             };
41049             var triggerIndex = 'Trigger'+(index+1);
41050
41051             if(this['hide'+triggerIndex]){
41052                 t.dom.style.display = 'none';
41053             }
41054             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41055             t.addClassOnOver('x-form-trigger-over');
41056             t.addClassOnClick('x-form-trigger-click');
41057         }, this);
41058         this.triggers = ts.elements;
41059     },
41060
41061     onTrigger1Click : Roo.emptyFn,
41062     onTrigger2Click : Roo.emptyFn
41063 });/*
41064  * Based on:
41065  * Ext JS Library 1.1.1
41066  * Copyright(c) 2006-2007, Ext JS, LLC.
41067  *
41068  * Originally Released Under LGPL - original licence link has changed is not relivant.
41069  *
41070  * Fork - LGPL
41071  * <script type="text/javascript">
41072  */
41073  
41074 /**
41075  * @class Roo.form.TextArea
41076  * @extends Roo.form.TextField
41077  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41078  * support for auto-sizing.
41079  * @constructor
41080  * Creates a new TextArea
41081  * @param {Object} config Configuration options
41082  */
41083 Roo.form.TextArea = function(config){
41084     Roo.form.TextArea.superclass.constructor.call(this, config);
41085     // these are provided exchanges for backwards compat
41086     // minHeight/maxHeight were replaced by growMin/growMax to be
41087     // compatible with TextField growing config values
41088     if(this.minHeight !== undefined){
41089         this.growMin = this.minHeight;
41090     }
41091     if(this.maxHeight !== undefined){
41092         this.growMax = this.maxHeight;
41093     }
41094 };
41095
41096 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41097     /**
41098      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41099      */
41100     growMin : 60,
41101     /**
41102      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41103      */
41104     growMax: 1000,
41105     /**
41106      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41107      * in the field (equivalent to setting overflow: hidden, defaults to false)
41108      */
41109     preventScrollbars: false,
41110     /**
41111      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41112      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41113      */
41114
41115     // private
41116     onRender : function(ct, position){
41117         if(!this.el){
41118             this.defaultAutoCreate = {
41119                 tag: "textarea",
41120                 style:"width:300px;height:60px;",
41121                 autocomplete: "new-password"
41122             };
41123         }
41124         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41125         if(this.grow){
41126             this.textSizeEl = Roo.DomHelper.append(document.body, {
41127                 tag: "pre", cls: "x-form-grow-sizer"
41128             });
41129             if(this.preventScrollbars){
41130                 this.el.setStyle("overflow", "hidden");
41131             }
41132             this.el.setHeight(this.growMin);
41133         }
41134     },
41135
41136     onDestroy : function(){
41137         if(this.textSizeEl){
41138             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41139         }
41140         Roo.form.TextArea.superclass.onDestroy.call(this);
41141     },
41142
41143     // private
41144     onKeyUp : function(e){
41145         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41146             this.autoSize();
41147         }
41148     },
41149
41150     /**
41151      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41152      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41153      */
41154     autoSize : function(){
41155         if(!this.grow || !this.textSizeEl){
41156             return;
41157         }
41158         var el = this.el;
41159         var v = el.dom.value;
41160         var ts = this.textSizeEl;
41161
41162         ts.innerHTML = '';
41163         ts.appendChild(document.createTextNode(v));
41164         v = ts.innerHTML;
41165
41166         Roo.fly(ts).setWidth(this.el.getWidth());
41167         if(v.length < 1){
41168             v = "&#160;&#160;";
41169         }else{
41170             if(Roo.isIE){
41171                 v = v.replace(/\n/g, '<p>&#160;</p>');
41172             }
41173             v += "&#160;\n&#160;";
41174         }
41175         ts.innerHTML = v;
41176         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41177         if(h != this.lastHeight){
41178             this.lastHeight = h;
41179             this.el.setHeight(h);
41180             this.fireEvent("autosize", this, h);
41181         }
41182     }
41183 });/*
41184  * Based on:
41185  * Ext JS Library 1.1.1
41186  * Copyright(c) 2006-2007, Ext JS, LLC.
41187  *
41188  * Originally Released Under LGPL - original licence link has changed is not relivant.
41189  *
41190  * Fork - LGPL
41191  * <script type="text/javascript">
41192  */
41193  
41194
41195 /**
41196  * @class Roo.form.NumberField
41197  * @extends Roo.form.TextField
41198  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41199  * @constructor
41200  * Creates a new NumberField
41201  * @param {Object} config Configuration options
41202  */
41203 Roo.form.NumberField = function(config){
41204     Roo.form.NumberField.superclass.constructor.call(this, config);
41205 };
41206
41207 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41208     /**
41209      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41210      */
41211     fieldClass: "x-form-field x-form-num-field",
41212     /**
41213      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41214      */
41215     allowDecimals : true,
41216     /**
41217      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41218      */
41219     decimalSeparator : ".",
41220     /**
41221      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41222      */
41223     decimalPrecision : 2,
41224     /**
41225      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41226      */
41227     allowNegative : true,
41228     /**
41229      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41230      */
41231     minValue : Number.NEGATIVE_INFINITY,
41232     /**
41233      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41234      */
41235     maxValue : Number.MAX_VALUE,
41236     /**
41237      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41238      */
41239     minText : "The minimum value for this field is {0}",
41240     /**
41241      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41242      */
41243     maxText : "The maximum value for this field is {0}",
41244     /**
41245      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41246      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41247      */
41248     nanText : "{0} is not a valid number",
41249
41250     // private
41251     initEvents : function(){
41252         Roo.form.NumberField.superclass.initEvents.call(this);
41253         var allowed = "0123456789";
41254         if(this.allowDecimals){
41255             allowed += this.decimalSeparator;
41256         }
41257         if(this.allowNegative){
41258             allowed += "-";
41259         }
41260         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41261         var keyPress = function(e){
41262             var k = e.getKey();
41263             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41264                 return;
41265             }
41266             var c = e.getCharCode();
41267             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41268                 e.stopEvent();
41269             }
41270         };
41271         this.el.on("keypress", keyPress, this);
41272     },
41273
41274     // private
41275     validateValue : function(value){
41276         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41277             return false;
41278         }
41279         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41280              return true;
41281         }
41282         var num = this.parseValue(value);
41283         if(isNaN(num)){
41284             this.markInvalid(String.format(this.nanText, value));
41285             return false;
41286         }
41287         if(num < this.minValue){
41288             this.markInvalid(String.format(this.minText, this.minValue));
41289             return false;
41290         }
41291         if(num > this.maxValue){
41292             this.markInvalid(String.format(this.maxText, this.maxValue));
41293             return false;
41294         }
41295         return true;
41296     },
41297
41298     getValue : function(){
41299         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41300     },
41301
41302     // private
41303     parseValue : function(value){
41304         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41305         return isNaN(value) ? '' : value;
41306     },
41307
41308     // private
41309     fixPrecision : function(value){
41310         var nan = isNaN(value);
41311         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41312             return nan ? '' : value;
41313         }
41314         return parseFloat(value).toFixed(this.decimalPrecision);
41315     },
41316
41317     setValue : function(v){
41318         v = this.fixPrecision(v);
41319         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41320     },
41321
41322     // private
41323     decimalPrecisionFcn : function(v){
41324         return Math.floor(v);
41325     },
41326
41327     beforeBlur : function(){
41328         var v = this.parseValue(this.getRawValue());
41329         if(v){
41330             this.setValue(v);
41331         }
41332     }
41333 });/*
41334  * Based on:
41335  * Ext JS Library 1.1.1
41336  * Copyright(c) 2006-2007, Ext JS, LLC.
41337  *
41338  * Originally Released Under LGPL - original licence link has changed is not relivant.
41339  *
41340  * Fork - LGPL
41341  * <script type="text/javascript">
41342  */
41343  
41344 /**
41345  * @class Roo.form.DateField
41346  * @extends Roo.form.TriggerField
41347  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41348 * @constructor
41349 * Create a new DateField
41350 * @param {Object} config
41351  */
41352 Roo.form.DateField = function(config)
41353 {
41354     Roo.form.DateField.superclass.constructor.call(this, config);
41355     
41356       this.addEvents({
41357          
41358         /**
41359          * @event select
41360          * Fires when a date is selected
41361              * @param {Roo.form.DateField} combo This combo box
41362              * @param {Date} date The date selected
41363              */
41364         'select' : true
41365          
41366     });
41367     
41368     
41369     if(typeof this.minValue == "string") {
41370         this.minValue = this.parseDate(this.minValue);
41371     }
41372     if(typeof this.maxValue == "string") {
41373         this.maxValue = this.parseDate(this.maxValue);
41374     }
41375     this.ddMatch = null;
41376     if(this.disabledDates){
41377         var dd = this.disabledDates;
41378         var re = "(?:";
41379         for(var i = 0; i < dd.length; i++){
41380             re += dd[i];
41381             if(i != dd.length-1) {
41382                 re += "|";
41383             }
41384         }
41385         this.ddMatch = new RegExp(re + ")");
41386     }
41387 };
41388
41389 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41390     /**
41391      * @cfg {String} format
41392      * The default date format string which can be overriden for localization support.  The format must be
41393      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41394      */
41395     format : "m/d/y",
41396     /**
41397      * @cfg {String} altFormats
41398      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41399      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41400      */
41401     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41402     /**
41403      * @cfg {Array} disabledDays
41404      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41405      */
41406     disabledDays : null,
41407     /**
41408      * @cfg {String} disabledDaysText
41409      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41410      */
41411     disabledDaysText : "Disabled",
41412     /**
41413      * @cfg {Array} disabledDates
41414      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41415      * expression so they are very powerful. Some examples:
41416      * <ul>
41417      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41418      * <li>["03/08", "09/16"] would disable those days for every year</li>
41419      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41420      * <li>["03/../2006"] would disable every day in March 2006</li>
41421      * <li>["^03"] would disable every day in every March</li>
41422      * </ul>
41423      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41424      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41425      */
41426     disabledDates : null,
41427     /**
41428      * @cfg {String} disabledDatesText
41429      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41430      */
41431     disabledDatesText : "Disabled",
41432     /**
41433      * @cfg {Date/String} minValue
41434      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41435      * valid format (defaults to null).
41436      */
41437     minValue : null,
41438     /**
41439      * @cfg {Date/String} maxValue
41440      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41441      * valid format (defaults to null).
41442      */
41443     maxValue : null,
41444     /**
41445      * @cfg {String} minText
41446      * The error text to display when the date in the cell is before minValue (defaults to
41447      * 'The date in this field must be after {minValue}').
41448      */
41449     minText : "The date in this field must be equal to or after {0}",
41450     /**
41451      * @cfg {String} maxText
41452      * The error text to display when the date in the cell is after maxValue (defaults to
41453      * 'The date in this field must be before {maxValue}').
41454      */
41455     maxText : "The date in this field must be equal to or before {0}",
41456     /**
41457      * @cfg {String} invalidText
41458      * The error text to display when the date in the field is invalid (defaults to
41459      * '{value} is not a valid date - it must be in the format {format}').
41460      */
41461     invalidText : "{0} is not a valid date - it must be in the format {1}",
41462     /**
41463      * @cfg {String} triggerClass
41464      * An additional CSS class used to style the trigger button.  The trigger will always get the
41465      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41466      * which displays a calendar icon).
41467      */
41468     triggerClass : 'x-form-date-trigger',
41469     
41470
41471     /**
41472      * @cfg {Boolean} useIso
41473      * if enabled, then the date field will use a hidden field to store the 
41474      * real value as iso formated date. default (false)
41475      */ 
41476     useIso : false,
41477     /**
41478      * @cfg {String/Object} autoCreate
41479      * A DomHelper element spec, or true for a default element spec (defaults to
41480      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41481      */ 
41482     // private
41483     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41484     
41485     // private
41486     hiddenField: false,
41487     
41488     onRender : function(ct, position)
41489     {
41490         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41491         if (this.useIso) {
41492             //this.el.dom.removeAttribute('name'); 
41493             Roo.log("Changing name?");
41494             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41495             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41496                     'before', true);
41497             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41498             // prevent input submission
41499             this.hiddenName = this.name;
41500         }
41501             
41502             
41503     },
41504     
41505     // private
41506     validateValue : function(value)
41507     {
41508         value = this.formatDate(value);
41509         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41510             Roo.log('super failed');
41511             return false;
41512         }
41513         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41514              return true;
41515         }
41516         var svalue = value;
41517         value = this.parseDate(value);
41518         if(!value){
41519             Roo.log('parse date failed' + svalue);
41520             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41521             return false;
41522         }
41523         var time = value.getTime();
41524         if(this.minValue && time < this.minValue.getTime()){
41525             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41526             return false;
41527         }
41528         if(this.maxValue && time > this.maxValue.getTime()){
41529             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41530             return false;
41531         }
41532         if(this.disabledDays){
41533             var day = value.getDay();
41534             for(var i = 0; i < this.disabledDays.length; i++) {
41535                 if(day === this.disabledDays[i]){
41536                     this.markInvalid(this.disabledDaysText);
41537                     return false;
41538                 }
41539             }
41540         }
41541         var fvalue = this.formatDate(value);
41542         if(this.ddMatch && this.ddMatch.test(fvalue)){
41543             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41544             return false;
41545         }
41546         return true;
41547     },
41548
41549     // private
41550     // Provides logic to override the default TriggerField.validateBlur which just returns true
41551     validateBlur : function(){
41552         return !this.menu || !this.menu.isVisible();
41553     },
41554     
41555     getName: function()
41556     {
41557         // returns hidden if it's set..
41558         if (!this.rendered) {return ''};
41559         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41560         
41561     },
41562
41563     /**
41564      * Returns the current date value of the date field.
41565      * @return {Date} The date value
41566      */
41567     getValue : function(){
41568         
41569         return  this.hiddenField ?
41570                 this.hiddenField.value :
41571                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41572     },
41573
41574     /**
41575      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41576      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41577      * (the default format used is "m/d/y").
41578      * <br />Usage:
41579      * <pre><code>
41580 //All of these calls set the same date value (May 4, 2006)
41581
41582 //Pass a date object:
41583 var dt = new Date('5/4/06');
41584 dateField.setValue(dt);
41585
41586 //Pass a date string (default format):
41587 dateField.setValue('5/4/06');
41588
41589 //Pass a date string (custom format):
41590 dateField.format = 'Y-m-d';
41591 dateField.setValue('2006-5-4');
41592 </code></pre>
41593      * @param {String/Date} date The date or valid date string
41594      */
41595     setValue : function(date){
41596         if (this.hiddenField) {
41597             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41598         }
41599         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41600         // make sure the value field is always stored as a date..
41601         this.value = this.parseDate(date);
41602         
41603         
41604     },
41605
41606     // private
41607     parseDate : function(value){
41608         if(!value || value instanceof Date){
41609             return value;
41610         }
41611         var v = Date.parseDate(value, this.format);
41612          if (!v && this.useIso) {
41613             v = Date.parseDate(value, 'Y-m-d');
41614         }
41615         if(!v && this.altFormats){
41616             if(!this.altFormatsArray){
41617                 this.altFormatsArray = this.altFormats.split("|");
41618             }
41619             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41620                 v = Date.parseDate(value, this.altFormatsArray[i]);
41621             }
41622         }
41623         return v;
41624     },
41625
41626     // private
41627     formatDate : function(date, fmt){
41628         return (!date || !(date instanceof Date)) ?
41629                date : date.dateFormat(fmt || this.format);
41630     },
41631
41632     // private
41633     menuListeners : {
41634         select: function(m, d){
41635             
41636             this.setValue(d);
41637             this.fireEvent('select', this, d);
41638         },
41639         show : function(){ // retain focus styling
41640             this.onFocus();
41641         },
41642         hide : function(){
41643             this.focus.defer(10, this);
41644             var ml = this.menuListeners;
41645             this.menu.un("select", ml.select,  this);
41646             this.menu.un("show", ml.show,  this);
41647             this.menu.un("hide", ml.hide,  this);
41648         }
41649     },
41650
41651     // private
41652     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41653     onTriggerClick : function(){
41654         if(this.disabled){
41655             return;
41656         }
41657         if(this.menu == null){
41658             this.menu = new Roo.menu.DateMenu();
41659         }
41660         Roo.apply(this.menu.picker,  {
41661             showClear: this.allowBlank,
41662             minDate : this.minValue,
41663             maxDate : this.maxValue,
41664             disabledDatesRE : this.ddMatch,
41665             disabledDatesText : this.disabledDatesText,
41666             disabledDays : this.disabledDays,
41667             disabledDaysText : this.disabledDaysText,
41668             format : this.useIso ? 'Y-m-d' : this.format,
41669             minText : String.format(this.minText, this.formatDate(this.minValue)),
41670             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41671         });
41672         this.menu.on(Roo.apply({}, this.menuListeners, {
41673             scope:this
41674         }));
41675         this.menu.picker.setValue(this.getValue() || new Date());
41676         this.menu.show(this.el, "tl-bl?");
41677     },
41678
41679     beforeBlur : function(){
41680         var v = this.parseDate(this.getRawValue());
41681         if(v){
41682             this.setValue(v);
41683         }
41684     },
41685
41686     /*@
41687      * overide
41688      * 
41689      */
41690     isDirty : function() {
41691         if(this.disabled) {
41692             return false;
41693         }
41694         
41695         if(typeof(this.startValue) === 'undefined'){
41696             return false;
41697         }
41698         
41699         return String(this.getValue()) !== String(this.startValue);
41700         
41701     },
41702     // @overide
41703     cleanLeadingSpace : function(e)
41704     {
41705        return;
41706     }
41707     
41708 });/*
41709  * Based on:
41710  * Ext JS Library 1.1.1
41711  * Copyright(c) 2006-2007, Ext JS, LLC.
41712  *
41713  * Originally Released Under LGPL - original licence link has changed is not relivant.
41714  *
41715  * Fork - LGPL
41716  * <script type="text/javascript">
41717  */
41718  
41719 /**
41720  * @class Roo.form.MonthField
41721  * @extends Roo.form.TriggerField
41722  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41723 * @constructor
41724 * Create a new MonthField
41725 * @param {Object} config
41726  */
41727 Roo.form.MonthField = function(config){
41728     
41729     Roo.form.MonthField.superclass.constructor.call(this, config);
41730     
41731       this.addEvents({
41732          
41733         /**
41734          * @event select
41735          * Fires when a date is selected
41736              * @param {Roo.form.MonthFieeld} combo This combo box
41737              * @param {Date} date The date selected
41738              */
41739         'select' : true
41740          
41741     });
41742     
41743     
41744     if(typeof this.minValue == "string") {
41745         this.minValue = this.parseDate(this.minValue);
41746     }
41747     if(typeof this.maxValue == "string") {
41748         this.maxValue = this.parseDate(this.maxValue);
41749     }
41750     this.ddMatch = null;
41751     if(this.disabledDates){
41752         var dd = this.disabledDates;
41753         var re = "(?:";
41754         for(var i = 0; i < dd.length; i++){
41755             re += dd[i];
41756             if(i != dd.length-1) {
41757                 re += "|";
41758             }
41759         }
41760         this.ddMatch = new RegExp(re + ")");
41761     }
41762 };
41763
41764 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41765     /**
41766      * @cfg {String} format
41767      * The default date format string which can be overriden for localization support.  The format must be
41768      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41769      */
41770     format : "M Y",
41771     /**
41772      * @cfg {String} altFormats
41773      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41774      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41775      */
41776     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41777     /**
41778      * @cfg {Array} disabledDays
41779      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41780      */
41781     disabledDays : [0,1,2,3,4,5,6],
41782     /**
41783      * @cfg {String} disabledDaysText
41784      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41785      */
41786     disabledDaysText : "Disabled",
41787     /**
41788      * @cfg {Array} disabledDates
41789      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41790      * expression so they are very powerful. Some examples:
41791      * <ul>
41792      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41793      * <li>["03/08", "09/16"] would disable those days for every year</li>
41794      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41795      * <li>["03/../2006"] would disable every day in March 2006</li>
41796      * <li>["^03"] would disable every day in every March</li>
41797      * </ul>
41798      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41799      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41800      */
41801     disabledDates : null,
41802     /**
41803      * @cfg {String} disabledDatesText
41804      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41805      */
41806     disabledDatesText : "Disabled",
41807     /**
41808      * @cfg {Date/String} minValue
41809      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41810      * valid format (defaults to null).
41811      */
41812     minValue : null,
41813     /**
41814      * @cfg {Date/String} maxValue
41815      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41816      * valid format (defaults to null).
41817      */
41818     maxValue : null,
41819     /**
41820      * @cfg {String} minText
41821      * The error text to display when the date in the cell is before minValue (defaults to
41822      * 'The date in this field must be after {minValue}').
41823      */
41824     minText : "The date in this field must be equal to or after {0}",
41825     /**
41826      * @cfg {String} maxTextf
41827      * The error text to display when the date in the cell is after maxValue (defaults to
41828      * 'The date in this field must be before {maxValue}').
41829      */
41830     maxText : "The date in this field must be equal to or before {0}",
41831     /**
41832      * @cfg {String} invalidText
41833      * The error text to display when the date in the field is invalid (defaults to
41834      * '{value} is not a valid date - it must be in the format {format}').
41835      */
41836     invalidText : "{0} is not a valid date - it must be in the format {1}",
41837     /**
41838      * @cfg {String} triggerClass
41839      * An additional CSS class used to style the trigger button.  The trigger will always get the
41840      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41841      * which displays a calendar icon).
41842      */
41843     triggerClass : 'x-form-date-trigger',
41844     
41845
41846     /**
41847      * @cfg {Boolean} useIso
41848      * if enabled, then the date field will use a hidden field to store the 
41849      * real value as iso formated date. default (true)
41850      */ 
41851     useIso : true,
41852     /**
41853      * @cfg {String/Object} autoCreate
41854      * A DomHelper element spec, or true for a default element spec (defaults to
41855      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41856      */ 
41857     // private
41858     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41859     
41860     // private
41861     hiddenField: false,
41862     
41863     hideMonthPicker : false,
41864     
41865     onRender : function(ct, position)
41866     {
41867         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41868         if (this.useIso) {
41869             this.el.dom.removeAttribute('name'); 
41870             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41871                     'before', true);
41872             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41873             // prevent input submission
41874             this.hiddenName = this.name;
41875         }
41876             
41877             
41878     },
41879     
41880     // private
41881     validateValue : function(value)
41882     {
41883         value = this.formatDate(value);
41884         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41885             return false;
41886         }
41887         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41888              return true;
41889         }
41890         var svalue = value;
41891         value = this.parseDate(value);
41892         if(!value){
41893             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41894             return false;
41895         }
41896         var time = value.getTime();
41897         if(this.minValue && time < this.minValue.getTime()){
41898             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41899             return false;
41900         }
41901         if(this.maxValue && time > this.maxValue.getTime()){
41902             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41903             return false;
41904         }
41905         /*if(this.disabledDays){
41906             var day = value.getDay();
41907             for(var i = 0; i < this.disabledDays.length; i++) {
41908                 if(day === this.disabledDays[i]){
41909                     this.markInvalid(this.disabledDaysText);
41910                     return false;
41911                 }
41912             }
41913         }
41914         */
41915         var fvalue = this.formatDate(value);
41916         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41917             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41918             return false;
41919         }
41920         */
41921         return true;
41922     },
41923
41924     // private
41925     // Provides logic to override the default TriggerField.validateBlur which just returns true
41926     validateBlur : function(){
41927         return !this.menu || !this.menu.isVisible();
41928     },
41929
41930     /**
41931      * Returns the current date value of the date field.
41932      * @return {Date} The date value
41933      */
41934     getValue : function(){
41935         
41936         
41937         
41938         return  this.hiddenField ?
41939                 this.hiddenField.value :
41940                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41941     },
41942
41943     /**
41944      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41945      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41946      * (the default format used is "m/d/y").
41947      * <br />Usage:
41948      * <pre><code>
41949 //All of these calls set the same date value (May 4, 2006)
41950
41951 //Pass a date object:
41952 var dt = new Date('5/4/06');
41953 monthField.setValue(dt);
41954
41955 //Pass a date string (default format):
41956 monthField.setValue('5/4/06');
41957
41958 //Pass a date string (custom format):
41959 monthField.format = 'Y-m-d';
41960 monthField.setValue('2006-5-4');
41961 </code></pre>
41962      * @param {String/Date} date The date or valid date string
41963      */
41964     setValue : function(date){
41965         Roo.log('month setValue' + date);
41966         // can only be first of month..
41967         
41968         var val = this.parseDate(date);
41969         
41970         if (this.hiddenField) {
41971             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41972         }
41973         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41974         this.value = this.parseDate(date);
41975     },
41976
41977     // private
41978     parseDate : function(value){
41979         if(!value || value instanceof Date){
41980             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41981             return value;
41982         }
41983         var v = Date.parseDate(value, this.format);
41984         if (!v && this.useIso) {
41985             v = Date.parseDate(value, 'Y-m-d');
41986         }
41987         if (v) {
41988             // 
41989             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41990         }
41991         
41992         
41993         if(!v && this.altFormats){
41994             if(!this.altFormatsArray){
41995                 this.altFormatsArray = this.altFormats.split("|");
41996             }
41997             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41998                 v = Date.parseDate(value, this.altFormatsArray[i]);
41999             }
42000         }
42001         return v;
42002     },
42003
42004     // private
42005     formatDate : function(date, fmt){
42006         return (!date || !(date instanceof Date)) ?
42007                date : date.dateFormat(fmt || this.format);
42008     },
42009
42010     // private
42011     menuListeners : {
42012         select: function(m, d){
42013             this.setValue(d);
42014             this.fireEvent('select', this, d);
42015         },
42016         show : function(){ // retain focus styling
42017             this.onFocus();
42018         },
42019         hide : function(){
42020             this.focus.defer(10, this);
42021             var ml = this.menuListeners;
42022             this.menu.un("select", ml.select,  this);
42023             this.menu.un("show", ml.show,  this);
42024             this.menu.un("hide", ml.hide,  this);
42025         }
42026     },
42027     // private
42028     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42029     onTriggerClick : function(){
42030         if(this.disabled){
42031             return;
42032         }
42033         if(this.menu == null){
42034             this.menu = new Roo.menu.DateMenu();
42035            
42036         }
42037         
42038         Roo.apply(this.menu.picker,  {
42039             
42040             showClear: this.allowBlank,
42041             minDate : this.minValue,
42042             maxDate : this.maxValue,
42043             disabledDatesRE : this.ddMatch,
42044             disabledDatesText : this.disabledDatesText,
42045             
42046             format : this.useIso ? 'Y-m-d' : this.format,
42047             minText : String.format(this.minText, this.formatDate(this.minValue)),
42048             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42049             
42050         });
42051          this.menu.on(Roo.apply({}, this.menuListeners, {
42052             scope:this
42053         }));
42054        
42055         
42056         var m = this.menu;
42057         var p = m.picker;
42058         
42059         // hide month picker get's called when we called by 'before hide';
42060         
42061         var ignorehide = true;
42062         p.hideMonthPicker  = function(disableAnim){
42063             if (ignorehide) {
42064                 return;
42065             }
42066              if(this.monthPicker){
42067                 Roo.log("hideMonthPicker called");
42068                 if(disableAnim === true){
42069                     this.monthPicker.hide();
42070                 }else{
42071                     this.monthPicker.slideOut('t', {duration:.2});
42072                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42073                     p.fireEvent("select", this, this.value);
42074                     m.hide();
42075                 }
42076             }
42077         }
42078         
42079         Roo.log('picker set value');
42080         Roo.log(this.getValue());
42081         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42082         m.show(this.el, 'tl-bl?');
42083         ignorehide  = false;
42084         // this will trigger hideMonthPicker..
42085         
42086         
42087         // hidden the day picker
42088         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42089         
42090         
42091         
42092       
42093         
42094         p.showMonthPicker.defer(100, p);
42095     
42096         
42097        
42098     },
42099
42100     beforeBlur : function(){
42101         var v = this.parseDate(this.getRawValue());
42102         if(v){
42103             this.setValue(v);
42104         }
42105     }
42106
42107     /** @cfg {Boolean} grow @hide */
42108     /** @cfg {Number} growMin @hide */
42109     /** @cfg {Number} growMax @hide */
42110     /**
42111      * @hide
42112      * @method autoSize
42113      */
42114 });/*
42115  * Based on:
42116  * Ext JS Library 1.1.1
42117  * Copyright(c) 2006-2007, Ext JS, LLC.
42118  *
42119  * Originally Released Under LGPL - original licence link has changed is not relivant.
42120  *
42121  * Fork - LGPL
42122  * <script type="text/javascript">
42123  */
42124  
42125
42126 /**
42127  * @class Roo.form.ComboBox
42128  * @extends Roo.form.TriggerField
42129  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42130  * @constructor
42131  * Create a new ComboBox.
42132  * @param {Object} config Configuration options
42133  */
42134 Roo.form.ComboBox = function(config){
42135     Roo.form.ComboBox.superclass.constructor.call(this, config);
42136     this.addEvents({
42137         /**
42138          * @event expand
42139          * Fires when the dropdown list is expanded
42140              * @param {Roo.form.ComboBox} combo This combo box
42141              */
42142         'expand' : true,
42143         /**
42144          * @event collapse
42145          * Fires when the dropdown list is collapsed
42146              * @param {Roo.form.ComboBox} combo This combo box
42147              */
42148         'collapse' : true,
42149         /**
42150          * @event beforeselect
42151          * Fires before a list item is selected. Return false to cancel the selection.
42152              * @param {Roo.form.ComboBox} combo This combo box
42153              * @param {Roo.data.Record} record The data record returned from the underlying store
42154              * @param {Number} index The index of the selected item in the dropdown list
42155              */
42156         'beforeselect' : true,
42157         /**
42158          * @event select
42159          * Fires when a list item is selected
42160              * @param {Roo.form.ComboBox} combo This combo box
42161              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42162              * @param {Number} index The index of the selected item in the dropdown list
42163              */
42164         'select' : true,
42165         /**
42166          * @event beforequery
42167          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42168          * The event object passed has these properties:
42169              * @param {Roo.form.ComboBox} combo This combo box
42170              * @param {String} query The query
42171              * @param {Boolean} forceAll true to force "all" query
42172              * @param {Boolean} cancel true to cancel the query
42173              * @param {Object} e The query event object
42174              */
42175         'beforequery': true,
42176          /**
42177          * @event add
42178          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42179              * @param {Roo.form.ComboBox} combo This combo box
42180              */
42181         'add' : true,
42182         /**
42183          * @event edit
42184          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42185              * @param {Roo.form.ComboBox} combo This combo box
42186              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42187              */
42188         'edit' : true
42189         
42190         
42191     });
42192     if(this.transform){
42193         this.allowDomMove = false;
42194         var s = Roo.getDom(this.transform);
42195         if(!this.hiddenName){
42196             this.hiddenName = s.name;
42197         }
42198         if(!this.store){
42199             this.mode = 'local';
42200             var d = [], opts = s.options;
42201             for(var i = 0, len = opts.length;i < len; i++){
42202                 var o = opts[i];
42203                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42204                 if(o.selected) {
42205                     this.value = value;
42206                 }
42207                 d.push([value, o.text]);
42208             }
42209             this.store = new Roo.data.SimpleStore({
42210                 'id': 0,
42211                 fields: ['value', 'text'],
42212                 data : d
42213             });
42214             this.valueField = 'value';
42215             this.displayField = 'text';
42216         }
42217         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42218         if(!this.lazyRender){
42219             this.target = true;
42220             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42221             s.parentNode.removeChild(s); // remove it
42222             this.render(this.el.parentNode);
42223         }else{
42224             s.parentNode.removeChild(s); // remove it
42225         }
42226
42227     }
42228     if (this.store) {
42229         this.store = Roo.factory(this.store, Roo.data);
42230     }
42231     
42232     this.selectedIndex = -1;
42233     if(this.mode == 'local'){
42234         if(config.queryDelay === undefined){
42235             this.queryDelay = 10;
42236         }
42237         if(config.minChars === undefined){
42238             this.minChars = 0;
42239         }
42240     }
42241 };
42242
42243 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42244     /**
42245      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42246      */
42247     /**
42248      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42249      * rendering into an Roo.Editor, defaults to false)
42250      */
42251     /**
42252      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42253      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42254      */
42255     /**
42256      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42257      */
42258     /**
42259      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42260      * the dropdown list (defaults to undefined, with no header element)
42261      */
42262
42263      /**
42264      * @cfg {String/Roo.Template} tpl The template to use to render the output
42265      */
42266      
42267     // private
42268     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42269     /**
42270      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42271      */
42272     listWidth: undefined,
42273     /**
42274      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42275      * mode = 'remote' or 'text' if mode = 'local')
42276      */
42277     displayField: undefined,
42278     /**
42279      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42280      * mode = 'remote' or 'value' if mode = 'local'). 
42281      * Note: use of a valueField requires the user make a selection
42282      * in order for a value to be mapped.
42283      */
42284     valueField: undefined,
42285     
42286     
42287     /**
42288      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42289      * field's data value (defaults to the underlying DOM element's name)
42290      */
42291     hiddenName: undefined,
42292     /**
42293      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42294      */
42295     listClass: '',
42296     /**
42297      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42298      */
42299     selectedClass: 'x-combo-selected',
42300     /**
42301      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42302      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42303      * which displays a downward arrow icon).
42304      */
42305     triggerClass : 'x-form-arrow-trigger',
42306     /**
42307      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42308      */
42309     shadow:'sides',
42310     /**
42311      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42312      * anchor positions (defaults to 'tl-bl')
42313      */
42314     listAlign: 'tl-bl?',
42315     /**
42316      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42317      */
42318     maxHeight: 300,
42319     /**
42320      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42321      * query specified by the allQuery config option (defaults to 'query')
42322      */
42323     triggerAction: 'query',
42324     /**
42325      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42326      * (defaults to 4, does not apply if editable = false)
42327      */
42328     minChars : 4,
42329     /**
42330      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42331      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42332      */
42333     typeAhead: false,
42334     /**
42335      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42336      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42337      */
42338     queryDelay: 500,
42339     /**
42340      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42341      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42342      */
42343     pageSize: 0,
42344     /**
42345      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42346      * when editable = true (defaults to false)
42347      */
42348     selectOnFocus:false,
42349     /**
42350      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42351      */
42352     queryParam: 'query',
42353     /**
42354      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42355      * when mode = 'remote' (defaults to 'Loading...')
42356      */
42357     loadingText: 'Loading...',
42358     /**
42359      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42360      */
42361     resizable: false,
42362     /**
42363      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42364      */
42365     handleHeight : 8,
42366     /**
42367      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42368      * traditional select (defaults to true)
42369      */
42370     editable: true,
42371     /**
42372      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42373      */
42374     allQuery: '',
42375     /**
42376      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42377      */
42378     mode: 'remote',
42379     /**
42380      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42381      * listWidth has a higher value)
42382      */
42383     minListWidth : 70,
42384     /**
42385      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42386      * allow the user to set arbitrary text into the field (defaults to false)
42387      */
42388     forceSelection:false,
42389     /**
42390      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42391      * if typeAhead = true (defaults to 250)
42392      */
42393     typeAheadDelay : 250,
42394     /**
42395      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42396      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42397      */
42398     valueNotFoundText : undefined,
42399     /**
42400      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42401      */
42402     blockFocus : false,
42403     
42404     /**
42405      * @cfg {Boolean} disableClear Disable showing of clear button.
42406      */
42407     disableClear : false,
42408     /**
42409      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42410      */
42411     alwaysQuery : false,
42412     
42413     //private
42414     addicon : false,
42415     editicon: false,
42416     
42417     // element that contains real text value.. (when hidden is used..)
42418      
42419     // private
42420     onRender : function(ct, position)
42421     {
42422         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42423         
42424         if(this.hiddenName){
42425             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42426                     'before', true);
42427             this.hiddenField.value =
42428                 this.hiddenValue !== undefined ? this.hiddenValue :
42429                 this.value !== undefined ? this.value : '';
42430
42431             // prevent input submission
42432             this.el.dom.removeAttribute('name');
42433              
42434              
42435         }
42436         
42437         if(Roo.isGecko){
42438             this.el.dom.setAttribute('autocomplete', 'off');
42439         }
42440
42441         var cls = 'x-combo-list';
42442
42443         this.list = new Roo.Layer({
42444             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42445         });
42446
42447         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42448         this.list.setWidth(lw);
42449         this.list.swallowEvent('mousewheel');
42450         this.assetHeight = 0;
42451
42452         if(this.title){
42453             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42454             this.assetHeight += this.header.getHeight();
42455         }
42456
42457         this.innerList = this.list.createChild({cls:cls+'-inner'});
42458         this.innerList.on('mouseover', this.onViewOver, this);
42459         this.innerList.on('mousemove', this.onViewMove, this);
42460         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42461         
42462         if(this.allowBlank && !this.pageSize && !this.disableClear){
42463             this.footer = this.list.createChild({cls:cls+'-ft'});
42464             this.pageTb = new Roo.Toolbar(this.footer);
42465            
42466         }
42467         if(this.pageSize){
42468             this.footer = this.list.createChild({cls:cls+'-ft'});
42469             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42470                     {pageSize: this.pageSize});
42471             
42472         }
42473         
42474         if (this.pageTb && this.allowBlank && !this.disableClear) {
42475             var _this = this;
42476             this.pageTb.add(new Roo.Toolbar.Fill(), {
42477                 cls: 'x-btn-icon x-btn-clear',
42478                 text: '&#160;',
42479                 handler: function()
42480                 {
42481                     _this.collapse();
42482                     _this.clearValue();
42483                     _this.onSelect(false, -1);
42484                 }
42485             });
42486         }
42487         if (this.footer) {
42488             this.assetHeight += this.footer.getHeight();
42489         }
42490         
42491
42492         if(!this.tpl){
42493             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42494         }
42495
42496         this.view = new Roo.View(this.innerList, this.tpl, {
42497             singleSelect:true,
42498             store: this.store,
42499             selectedClass: this.selectedClass
42500         });
42501
42502         this.view.on('click', this.onViewClick, this);
42503
42504         this.store.on('beforeload', this.onBeforeLoad, this);
42505         this.store.on('load', this.onLoad, this);
42506         this.store.on('loadexception', this.onLoadException, this);
42507
42508         if(this.resizable){
42509             this.resizer = new Roo.Resizable(this.list,  {
42510                pinned:true, handles:'se'
42511             });
42512             this.resizer.on('resize', function(r, w, h){
42513                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42514                 this.listWidth = w;
42515                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42516                 this.restrictHeight();
42517             }, this);
42518             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42519         }
42520         if(!this.editable){
42521             this.editable = true;
42522             this.setEditable(false);
42523         }  
42524         
42525         
42526         if (typeof(this.events.add.listeners) != 'undefined') {
42527             
42528             this.addicon = this.wrap.createChild(
42529                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42530        
42531             this.addicon.on('click', function(e) {
42532                 this.fireEvent('add', this);
42533             }, this);
42534         }
42535         if (typeof(this.events.edit.listeners) != 'undefined') {
42536             
42537             this.editicon = this.wrap.createChild(
42538                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42539             if (this.addicon) {
42540                 this.editicon.setStyle('margin-left', '40px');
42541             }
42542             this.editicon.on('click', function(e) {
42543                 
42544                 // we fire even  if inothing is selected..
42545                 this.fireEvent('edit', this, this.lastData );
42546                 
42547             }, this);
42548         }
42549         
42550         
42551         
42552     },
42553
42554     // private
42555     initEvents : function(){
42556         Roo.form.ComboBox.superclass.initEvents.call(this);
42557
42558         this.keyNav = new Roo.KeyNav(this.el, {
42559             "up" : function(e){
42560                 this.inKeyMode = true;
42561                 this.selectPrev();
42562             },
42563
42564             "down" : function(e){
42565                 if(!this.isExpanded()){
42566                     this.onTriggerClick();
42567                 }else{
42568                     this.inKeyMode = true;
42569                     this.selectNext();
42570                 }
42571             },
42572
42573             "enter" : function(e){
42574                 this.onViewClick();
42575                 //return true;
42576             },
42577
42578             "esc" : function(e){
42579                 this.collapse();
42580             },
42581
42582             "tab" : function(e){
42583                 this.onViewClick(false);
42584                 this.fireEvent("specialkey", this, e);
42585                 return true;
42586             },
42587
42588             scope : this,
42589
42590             doRelay : function(foo, bar, hname){
42591                 if(hname == 'down' || this.scope.isExpanded()){
42592                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42593                 }
42594                 return true;
42595             },
42596
42597             forceKeyDown: true
42598         });
42599         this.queryDelay = Math.max(this.queryDelay || 10,
42600                 this.mode == 'local' ? 10 : 250);
42601         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42602         if(this.typeAhead){
42603             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42604         }
42605         if(this.editable !== false){
42606             this.el.on("keyup", this.onKeyUp, this);
42607         }
42608         if(this.forceSelection){
42609             this.on('blur', this.doForce, this);
42610         }
42611     },
42612
42613     onDestroy : function(){
42614         if(this.view){
42615             this.view.setStore(null);
42616             this.view.el.removeAllListeners();
42617             this.view.el.remove();
42618             this.view.purgeListeners();
42619         }
42620         if(this.list){
42621             this.list.destroy();
42622         }
42623         if(this.store){
42624             this.store.un('beforeload', this.onBeforeLoad, this);
42625             this.store.un('load', this.onLoad, this);
42626             this.store.un('loadexception', this.onLoadException, this);
42627         }
42628         Roo.form.ComboBox.superclass.onDestroy.call(this);
42629     },
42630
42631     // private
42632     fireKey : function(e){
42633         if(e.isNavKeyPress() && !this.list.isVisible()){
42634             this.fireEvent("specialkey", this, e);
42635         }
42636     },
42637
42638     // private
42639     onResize: function(w, h){
42640         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42641         
42642         if(typeof w != 'number'){
42643             // we do not handle it!?!?
42644             return;
42645         }
42646         var tw = this.trigger.getWidth();
42647         tw += this.addicon ? this.addicon.getWidth() : 0;
42648         tw += this.editicon ? this.editicon.getWidth() : 0;
42649         var x = w - tw;
42650         this.el.setWidth( this.adjustWidth('input', x));
42651             
42652         this.trigger.setStyle('left', x+'px');
42653         
42654         if(this.list && this.listWidth === undefined){
42655             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42656             this.list.setWidth(lw);
42657             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42658         }
42659         
42660     
42661         
42662     },
42663
42664     /**
42665      * Allow or prevent the user from directly editing the field text.  If false is passed,
42666      * the user will only be able to select from the items defined in the dropdown list.  This method
42667      * is the runtime equivalent of setting the 'editable' config option at config time.
42668      * @param {Boolean} value True to allow the user to directly edit the field text
42669      */
42670     setEditable : function(value){
42671         if(value == this.editable){
42672             return;
42673         }
42674         this.editable = value;
42675         if(!value){
42676             this.el.dom.setAttribute('readOnly', true);
42677             this.el.on('mousedown', this.onTriggerClick,  this);
42678             this.el.addClass('x-combo-noedit');
42679         }else{
42680             this.el.dom.setAttribute('readOnly', false);
42681             this.el.un('mousedown', this.onTriggerClick,  this);
42682             this.el.removeClass('x-combo-noedit');
42683         }
42684     },
42685
42686     // private
42687     onBeforeLoad : function(){
42688         if(!this.hasFocus){
42689             return;
42690         }
42691         this.innerList.update(this.loadingText ?
42692                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42693         this.restrictHeight();
42694         this.selectedIndex = -1;
42695     },
42696
42697     // private
42698     onLoad : function(){
42699         if(!this.hasFocus){
42700             return;
42701         }
42702         if(this.store.getCount() > 0){
42703             this.expand();
42704             this.restrictHeight();
42705             if(this.lastQuery == this.allQuery){
42706                 if(this.editable){
42707                     this.el.dom.select();
42708                 }
42709                 if(!this.selectByValue(this.value, true)){
42710                     this.select(0, true);
42711                 }
42712             }else{
42713                 this.selectNext();
42714                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42715                     this.taTask.delay(this.typeAheadDelay);
42716                 }
42717             }
42718         }else{
42719             this.onEmptyResults();
42720         }
42721         //this.el.focus();
42722     },
42723     // private
42724     onLoadException : function()
42725     {
42726         this.collapse();
42727         Roo.log(this.store.reader.jsonData);
42728         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42729             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42730         }
42731         
42732         
42733     },
42734     // private
42735     onTypeAhead : function(){
42736         if(this.store.getCount() > 0){
42737             var r = this.store.getAt(0);
42738             var newValue = r.data[this.displayField];
42739             var len = newValue.length;
42740             var selStart = this.getRawValue().length;
42741             if(selStart != len){
42742                 this.setRawValue(newValue);
42743                 this.selectText(selStart, newValue.length);
42744             }
42745         }
42746     },
42747
42748     // private
42749     onSelect : function(record, index){
42750         if(this.fireEvent('beforeselect', this, record, index) !== false){
42751             this.setFromData(index > -1 ? record.data : false);
42752             this.collapse();
42753             this.fireEvent('select', this, record, index);
42754         }
42755     },
42756
42757     /**
42758      * Returns the currently selected field value or empty string if no value is set.
42759      * @return {String} value The selected value
42760      */
42761     getValue : function(){
42762         if(this.valueField){
42763             return typeof this.value != 'undefined' ? this.value : '';
42764         }
42765         return Roo.form.ComboBox.superclass.getValue.call(this);
42766     },
42767
42768     /**
42769      * Clears any text/value currently set in the field
42770      */
42771     clearValue : function(){
42772         if(this.hiddenField){
42773             this.hiddenField.value = '';
42774         }
42775         this.value = '';
42776         this.setRawValue('');
42777         this.lastSelectionText = '';
42778         
42779     },
42780
42781     /**
42782      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42783      * will be displayed in the field.  If the value does not match the data value of an existing item,
42784      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42785      * Otherwise the field will be blank (although the value will still be set).
42786      * @param {String} value The value to match
42787      */
42788     setValue : function(v){
42789         var text = v;
42790         if(this.valueField){
42791             var r = this.findRecord(this.valueField, v);
42792             if(r){
42793                 text = r.data[this.displayField];
42794             }else if(this.valueNotFoundText !== undefined){
42795                 text = this.valueNotFoundText;
42796             }
42797         }
42798         this.lastSelectionText = text;
42799         if(this.hiddenField){
42800             this.hiddenField.value = v;
42801         }
42802         Roo.form.ComboBox.superclass.setValue.call(this, text);
42803         this.value = v;
42804     },
42805     /**
42806      * @property {Object} the last set data for the element
42807      */
42808     
42809     lastData : false,
42810     /**
42811      * Sets the value of the field based on a object which is related to the record format for the store.
42812      * @param {Object} value the value to set as. or false on reset?
42813      */
42814     setFromData : function(o){
42815         var dv = ''; // display value
42816         var vv = ''; // value value..
42817         this.lastData = o;
42818         if (this.displayField) {
42819             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42820         } else {
42821             // this is an error condition!!!
42822             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42823         }
42824         
42825         if(this.valueField){
42826             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42827         }
42828         if(this.hiddenField){
42829             this.hiddenField.value = vv;
42830             
42831             this.lastSelectionText = dv;
42832             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42833             this.value = vv;
42834             return;
42835         }
42836         // no hidden field.. - we store the value in 'value', but still display
42837         // display field!!!!
42838         this.lastSelectionText = dv;
42839         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42840         this.value = vv;
42841         
42842         
42843     },
42844     // private
42845     reset : function(){
42846         // overridden so that last data is reset..
42847         this.setValue(this.resetValue);
42848         this.originalValue = this.getValue();
42849         this.clearInvalid();
42850         this.lastData = false;
42851         if (this.view) {
42852             this.view.clearSelections();
42853         }
42854     },
42855     // private
42856     findRecord : function(prop, value){
42857         var record;
42858         if(this.store.getCount() > 0){
42859             this.store.each(function(r){
42860                 if(r.data[prop] == value){
42861                     record = r;
42862                     return false;
42863                 }
42864                 return true;
42865             });
42866         }
42867         return record;
42868     },
42869     
42870     getName: function()
42871     {
42872         // returns hidden if it's set..
42873         if (!this.rendered) {return ''};
42874         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42875         
42876     },
42877     // private
42878     onViewMove : function(e, t){
42879         this.inKeyMode = false;
42880     },
42881
42882     // private
42883     onViewOver : function(e, t){
42884         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42885             return;
42886         }
42887         var item = this.view.findItemFromChild(t);
42888         if(item){
42889             var index = this.view.indexOf(item);
42890             this.select(index, false);
42891         }
42892     },
42893
42894     // private
42895     onViewClick : function(doFocus)
42896     {
42897         var index = this.view.getSelectedIndexes()[0];
42898         var r = this.store.getAt(index);
42899         if(r){
42900             this.onSelect(r, index);
42901         }
42902         if(doFocus !== false && !this.blockFocus){
42903             this.el.focus();
42904         }
42905     },
42906
42907     // private
42908     restrictHeight : function(){
42909         this.innerList.dom.style.height = '';
42910         var inner = this.innerList.dom;
42911         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42912         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42913         this.list.beginUpdate();
42914         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42915         this.list.alignTo(this.el, this.listAlign);
42916         this.list.endUpdate();
42917     },
42918
42919     // private
42920     onEmptyResults : function(){
42921         this.collapse();
42922     },
42923
42924     /**
42925      * Returns true if the dropdown list is expanded, else false.
42926      */
42927     isExpanded : function(){
42928         return this.list.isVisible();
42929     },
42930
42931     /**
42932      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42933      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42934      * @param {String} value The data value of the item to select
42935      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42936      * selected item if it is not currently in view (defaults to true)
42937      * @return {Boolean} True if the value matched an item in the list, else false
42938      */
42939     selectByValue : function(v, scrollIntoView){
42940         if(v !== undefined && v !== null){
42941             var r = this.findRecord(this.valueField || this.displayField, v);
42942             if(r){
42943                 this.select(this.store.indexOf(r), scrollIntoView);
42944                 return true;
42945             }
42946         }
42947         return false;
42948     },
42949
42950     /**
42951      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42952      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42953      * @param {Number} index The zero-based index of the list item to select
42954      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42955      * selected item if it is not currently in view (defaults to true)
42956      */
42957     select : function(index, scrollIntoView){
42958         this.selectedIndex = index;
42959         this.view.select(index);
42960         if(scrollIntoView !== false){
42961             var el = this.view.getNode(index);
42962             if(el){
42963                 this.innerList.scrollChildIntoView(el, false);
42964             }
42965         }
42966     },
42967
42968     // private
42969     selectNext : function(){
42970         var ct = this.store.getCount();
42971         if(ct > 0){
42972             if(this.selectedIndex == -1){
42973                 this.select(0);
42974             }else if(this.selectedIndex < ct-1){
42975                 this.select(this.selectedIndex+1);
42976             }
42977         }
42978     },
42979
42980     // private
42981     selectPrev : function(){
42982         var ct = this.store.getCount();
42983         if(ct > 0){
42984             if(this.selectedIndex == -1){
42985                 this.select(0);
42986             }else if(this.selectedIndex != 0){
42987                 this.select(this.selectedIndex-1);
42988             }
42989         }
42990     },
42991
42992     // private
42993     onKeyUp : function(e){
42994         if(this.editable !== false && !e.isSpecialKey()){
42995             this.lastKey = e.getKey();
42996             this.dqTask.delay(this.queryDelay);
42997         }
42998     },
42999
43000     // private
43001     validateBlur : function(){
43002         return !this.list || !this.list.isVisible();   
43003     },
43004
43005     // private
43006     initQuery : function(){
43007         this.doQuery(this.getRawValue());
43008     },
43009
43010     // private
43011     doForce : function(){
43012         if(this.el.dom.value.length > 0){
43013             this.el.dom.value =
43014                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43015              
43016         }
43017     },
43018
43019     /**
43020      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43021      * query allowing the query action to be canceled if needed.
43022      * @param {String} query The SQL query to execute
43023      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43024      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43025      * saved in the current store (defaults to false)
43026      */
43027     doQuery : function(q, forceAll){
43028         if(q === undefined || q === null){
43029             q = '';
43030         }
43031         var qe = {
43032             query: q,
43033             forceAll: forceAll,
43034             combo: this,
43035             cancel:false
43036         };
43037         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43038             return false;
43039         }
43040         q = qe.query;
43041         forceAll = qe.forceAll;
43042         if(forceAll === true || (q.length >= this.minChars)){
43043             if(this.lastQuery != q || this.alwaysQuery){
43044                 this.lastQuery = q;
43045                 if(this.mode == 'local'){
43046                     this.selectedIndex = -1;
43047                     if(forceAll){
43048                         this.store.clearFilter();
43049                     }else{
43050                         this.store.filter(this.displayField, q);
43051                     }
43052                     this.onLoad();
43053                 }else{
43054                     this.store.baseParams[this.queryParam] = q;
43055                     this.store.load({
43056                         params: this.getParams(q)
43057                     });
43058                     this.expand();
43059                 }
43060             }else{
43061                 this.selectedIndex = -1;
43062                 this.onLoad();   
43063             }
43064         }
43065     },
43066
43067     // private
43068     getParams : function(q){
43069         var p = {};
43070         //p[this.queryParam] = q;
43071         if(this.pageSize){
43072             p.start = 0;
43073             p.limit = this.pageSize;
43074         }
43075         return p;
43076     },
43077
43078     /**
43079      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43080      */
43081     collapse : function(){
43082         if(!this.isExpanded()){
43083             return;
43084         }
43085         this.list.hide();
43086         Roo.get(document).un('mousedown', this.collapseIf, this);
43087         Roo.get(document).un('mousewheel', this.collapseIf, this);
43088         if (!this.editable) {
43089             Roo.get(document).un('keydown', this.listKeyPress, this);
43090         }
43091         this.fireEvent('collapse', this);
43092     },
43093
43094     // private
43095     collapseIf : function(e){
43096         if(!e.within(this.wrap) && !e.within(this.list)){
43097             this.collapse();
43098         }
43099     },
43100
43101     /**
43102      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43103      */
43104     expand : function(){
43105         if(this.isExpanded() || !this.hasFocus){
43106             return;
43107         }
43108         this.list.alignTo(this.el, this.listAlign);
43109         this.list.show();
43110         Roo.get(document).on('mousedown', this.collapseIf, this);
43111         Roo.get(document).on('mousewheel', this.collapseIf, this);
43112         if (!this.editable) {
43113             Roo.get(document).on('keydown', this.listKeyPress, this);
43114         }
43115         
43116         this.fireEvent('expand', this);
43117     },
43118
43119     // private
43120     // Implements the default empty TriggerField.onTriggerClick function
43121     onTriggerClick : function(){
43122         if(this.disabled){
43123             return;
43124         }
43125         if(this.isExpanded()){
43126             this.collapse();
43127             if (!this.blockFocus) {
43128                 this.el.focus();
43129             }
43130             
43131         }else {
43132             this.hasFocus = true;
43133             if(this.triggerAction == 'all') {
43134                 this.doQuery(this.allQuery, true);
43135             } else {
43136                 this.doQuery(this.getRawValue());
43137             }
43138             if (!this.blockFocus) {
43139                 this.el.focus();
43140             }
43141         }
43142     },
43143     listKeyPress : function(e)
43144     {
43145         //Roo.log('listkeypress');
43146         // scroll to first matching element based on key pres..
43147         if (e.isSpecialKey()) {
43148             return false;
43149         }
43150         var k = String.fromCharCode(e.getKey()).toUpperCase();
43151         //Roo.log(k);
43152         var match  = false;
43153         var csel = this.view.getSelectedNodes();
43154         var cselitem = false;
43155         if (csel.length) {
43156             var ix = this.view.indexOf(csel[0]);
43157             cselitem  = this.store.getAt(ix);
43158             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43159                 cselitem = false;
43160             }
43161             
43162         }
43163         
43164         this.store.each(function(v) { 
43165             if (cselitem) {
43166                 // start at existing selection.
43167                 if (cselitem.id == v.id) {
43168                     cselitem = false;
43169                 }
43170                 return;
43171             }
43172                 
43173             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43174                 match = this.store.indexOf(v);
43175                 return false;
43176             }
43177         }, this);
43178         
43179         if (match === false) {
43180             return true; // no more action?
43181         }
43182         // scroll to?
43183         this.view.select(match);
43184         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43185         sn.scrollIntoView(sn.dom.parentNode, false);
43186     } 
43187
43188     /** 
43189     * @cfg {Boolean} grow 
43190     * @hide 
43191     */
43192     /** 
43193     * @cfg {Number} growMin 
43194     * @hide 
43195     */
43196     /** 
43197     * @cfg {Number} growMax 
43198     * @hide 
43199     */
43200     /**
43201      * @hide
43202      * @method autoSize
43203      */
43204 });/*
43205  * Copyright(c) 2010-2012, Roo J Solutions Limited
43206  *
43207  * Licence LGPL
43208  *
43209  */
43210
43211 /**
43212  * @class Roo.form.ComboBoxArray
43213  * @extends Roo.form.TextField
43214  * A facebook style adder... for lists of email / people / countries  etc...
43215  * pick multiple items from a combo box, and shows each one.
43216  *
43217  *  Fred [x]  Brian [x]  [Pick another |v]
43218  *
43219  *
43220  *  For this to work: it needs various extra information
43221  *    - normal combo problay has
43222  *      name, hiddenName
43223  *    + displayField, valueField
43224  *
43225  *    For our purpose...
43226  *
43227  *
43228  *   If we change from 'extends' to wrapping...
43229  *   
43230  *  
43231  *
43232  
43233  
43234  * @constructor
43235  * Create a new ComboBoxArray.
43236  * @param {Object} config Configuration options
43237  */
43238  
43239
43240 Roo.form.ComboBoxArray = function(config)
43241 {
43242     this.addEvents({
43243         /**
43244          * @event beforeremove
43245          * Fires before remove the value from the list
43246              * @param {Roo.form.ComboBoxArray} _self This combo box array
43247              * @param {Roo.form.ComboBoxArray.Item} item removed item
43248              */
43249         'beforeremove' : true,
43250         /**
43251          * @event remove
43252          * Fires when remove the value from the list
43253              * @param {Roo.form.ComboBoxArray} _self This combo box array
43254              * @param {Roo.form.ComboBoxArray.Item} item removed item
43255              */
43256         'remove' : true
43257         
43258         
43259     });
43260     
43261     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43262     
43263     this.items = new Roo.util.MixedCollection(false);
43264     
43265     // construct the child combo...
43266     
43267     
43268     
43269     
43270    
43271     
43272 }
43273
43274  
43275 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43276
43277     /**
43278      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43279      */
43280     
43281     lastData : false,
43282     
43283     // behavies liek a hiddne field
43284     inputType:      'hidden',
43285     /**
43286      * @cfg {Number} width The width of the box that displays the selected element
43287      */ 
43288     width:          300,
43289
43290     
43291     
43292     /**
43293      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43294      */
43295     name : false,
43296     /**
43297      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43298      */
43299     hiddenName : false,
43300       /**
43301      * @cfg {String} seperator    The value seperator normally ',' 
43302      */
43303     seperator : ',',
43304     
43305     // private the array of items that are displayed..
43306     items  : false,
43307     // private - the hidden field el.
43308     hiddenEl : false,
43309     // private - the filed el..
43310     el : false,
43311     
43312     //validateValue : function() { return true; }, // all values are ok!
43313     //onAddClick: function() { },
43314     
43315     onRender : function(ct, position) 
43316     {
43317         
43318         // create the standard hidden element
43319         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43320         
43321         
43322         // give fake names to child combo;
43323         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43324         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43325         
43326         this.combo = Roo.factory(this.combo, Roo.form);
43327         this.combo.onRender(ct, position);
43328         if (typeof(this.combo.width) != 'undefined') {
43329             this.combo.onResize(this.combo.width,0);
43330         }
43331         
43332         this.combo.initEvents();
43333         
43334         // assigned so form know we need to do this..
43335         this.store          = this.combo.store;
43336         this.valueField     = this.combo.valueField;
43337         this.displayField   = this.combo.displayField ;
43338         
43339         
43340         this.combo.wrap.addClass('x-cbarray-grp');
43341         
43342         var cbwrap = this.combo.wrap.createChild(
43343             {tag: 'div', cls: 'x-cbarray-cb'},
43344             this.combo.el.dom
43345         );
43346         
43347              
43348         this.hiddenEl = this.combo.wrap.createChild({
43349             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43350         });
43351         this.el = this.combo.wrap.createChild({
43352             tag: 'input',  type:'hidden' , name: this.name, value : ''
43353         });
43354          //   this.el.dom.removeAttribute("name");
43355         
43356         
43357         this.outerWrap = this.combo.wrap;
43358         this.wrap = cbwrap;
43359         
43360         this.outerWrap.setWidth(this.width);
43361         this.outerWrap.dom.removeChild(this.el.dom);
43362         
43363         this.wrap.dom.appendChild(this.el.dom);
43364         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43365         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43366         
43367         this.combo.trigger.setStyle('position','relative');
43368         this.combo.trigger.setStyle('left', '0px');
43369         this.combo.trigger.setStyle('top', '2px');
43370         
43371         this.combo.el.setStyle('vertical-align', 'text-bottom');
43372         
43373         //this.trigger.setStyle('vertical-align', 'top');
43374         
43375         // this should use the code from combo really... on('add' ....)
43376         if (this.adder) {
43377             
43378         
43379             this.adder = this.outerWrap.createChild(
43380                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43381             var _t = this;
43382             this.adder.on('click', function(e) {
43383                 _t.fireEvent('adderclick', this, e);
43384             }, _t);
43385         }
43386         //var _t = this;
43387         //this.adder.on('click', this.onAddClick, _t);
43388         
43389         
43390         this.combo.on('select', function(cb, rec, ix) {
43391             this.addItem(rec.data);
43392             
43393             cb.setValue('');
43394             cb.el.dom.value = '';
43395             //cb.lastData = rec.data;
43396             // add to list
43397             
43398         }, this);
43399         
43400         
43401     },
43402     
43403     
43404     getName: function()
43405     {
43406         // returns hidden if it's set..
43407         if (!this.rendered) {return ''};
43408         return  this.hiddenName ? this.hiddenName : this.name;
43409         
43410     },
43411     
43412     
43413     onResize: function(w, h){
43414         
43415         return;
43416         // not sure if this is needed..
43417         //this.combo.onResize(w,h);
43418         
43419         if(typeof w != 'number'){
43420             // we do not handle it!?!?
43421             return;
43422         }
43423         var tw = this.combo.trigger.getWidth();
43424         tw += this.addicon ? this.addicon.getWidth() : 0;
43425         tw += this.editicon ? this.editicon.getWidth() : 0;
43426         var x = w - tw;
43427         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43428             
43429         this.combo.trigger.setStyle('left', '0px');
43430         
43431         if(this.list && this.listWidth === undefined){
43432             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43433             this.list.setWidth(lw);
43434             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43435         }
43436         
43437     
43438         
43439     },
43440     
43441     addItem: function(rec)
43442     {
43443         var valueField = this.combo.valueField;
43444         var displayField = this.combo.displayField;
43445         
43446         if (this.items.indexOfKey(rec[valueField]) > -1) {
43447             //console.log("GOT " + rec.data.id);
43448             return;
43449         }
43450         
43451         var x = new Roo.form.ComboBoxArray.Item({
43452             //id : rec[this.idField],
43453             data : rec,
43454             displayField : displayField ,
43455             tipField : displayField ,
43456             cb : this
43457         });
43458         // use the 
43459         this.items.add(rec[valueField],x);
43460         // add it before the element..
43461         this.updateHiddenEl();
43462         x.render(this.outerWrap, this.wrap.dom);
43463         // add the image handler..
43464     },
43465     
43466     updateHiddenEl : function()
43467     {
43468         this.validate();
43469         if (!this.hiddenEl) {
43470             return;
43471         }
43472         var ar = [];
43473         var idField = this.combo.valueField;
43474         
43475         this.items.each(function(f) {
43476             ar.push(f.data[idField]);
43477         });
43478         this.hiddenEl.dom.value = ar.join(this.seperator);
43479         this.validate();
43480     },
43481     
43482     reset : function()
43483     {
43484         this.items.clear();
43485         
43486         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43487            el.remove();
43488         });
43489         
43490         this.el.dom.value = '';
43491         if (this.hiddenEl) {
43492             this.hiddenEl.dom.value = '';
43493         }
43494         
43495     },
43496     getValue: function()
43497     {
43498         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43499     },
43500     setValue: function(v) // not a valid action - must use addItems..
43501     {
43502         
43503         this.reset();
43504          
43505         if (this.store.isLocal && (typeof(v) == 'string')) {
43506             // then we can use the store to find the values..
43507             // comma seperated at present.. this needs to allow JSON based encoding..
43508             this.hiddenEl.value  = v;
43509             var v_ar = [];
43510             Roo.each(v.split(this.seperator), function(k) {
43511                 Roo.log("CHECK " + this.valueField + ',' + k);
43512                 var li = this.store.query(this.valueField, k);
43513                 if (!li.length) {
43514                     return;
43515                 }
43516                 var add = {};
43517                 add[this.valueField] = k;
43518                 add[this.displayField] = li.item(0).data[this.displayField];
43519                 
43520                 this.addItem(add);
43521             }, this) 
43522              
43523         }
43524         if (typeof(v) == 'object' ) {
43525             // then let's assume it's an array of objects..
43526             Roo.each(v, function(l) {
43527                 var add = l;
43528                 if (typeof(l) == 'string') {
43529                     add = {};
43530                     add[this.valueField] = l;
43531                     add[this.displayField] = l
43532                 }
43533                 this.addItem(add);
43534             }, this);
43535              
43536         }
43537         
43538         
43539     },
43540     setFromData: function(v)
43541     {
43542         // this recieves an object, if setValues is called.
43543         this.reset();
43544         this.el.dom.value = v[this.displayField];
43545         this.hiddenEl.dom.value = v[this.valueField];
43546         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43547             return;
43548         }
43549         var kv = v[this.valueField];
43550         var dv = v[this.displayField];
43551         kv = typeof(kv) != 'string' ? '' : kv;
43552         dv = typeof(dv) != 'string' ? '' : dv;
43553         
43554         
43555         var keys = kv.split(this.seperator);
43556         var display = dv.split(this.seperator);
43557         for (var i = 0 ; i < keys.length; i++) {
43558             add = {};
43559             add[this.valueField] = keys[i];
43560             add[this.displayField] = display[i];
43561             this.addItem(add);
43562         }
43563       
43564         
43565     },
43566     
43567     /**
43568      * Validates the combox array value
43569      * @return {Boolean} True if the value is valid, else false
43570      */
43571     validate : function(){
43572         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43573             this.clearInvalid();
43574             return true;
43575         }
43576         return false;
43577     },
43578     
43579     validateValue : function(value){
43580         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43581         
43582     },
43583     
43584     /*@
43585      * overide
43586      * 
43587      */
43588     isDirty : function() {
43589         if(this.disabled) {
43590             return false;
43591         }
43592         
43593         try {
43594             var d = Roo.decode(String(this.originalValue));
43595         } catch (e) {
43596             return String(this.getValue()) !== String(this.originalValue);
43597         }
43598         
43599         var originalValue = [];
43600         
43601         for (var i = 0; i < d.length; i++){
43602             originalValue.push(d[i][this.valueField]);
43603         }
43604         
43605         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43606         
43607     }
43608     
43609 });
43610
43611
43612
43613 /**
43614  * @class Roo.form.ComboBoxArray.Item
43615  * @extends Roo.BoxComponent
43616  * A selected item in the list
43617  *  Fred [x]  Brian [x]  [Pick another |v]
43618  * 
43619  * @constructor
43620  * Create a new item.
43621  * @param {Object} config Configuration options
43622  */
43623  
43624 Roo.form.ComboBoxArray.Item = function(config) {
43625     config.id = Roo.id();
43626     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43627 }
43628
43629 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43630     data : {},
43631     cb: false,
43632     displayField : false,
43633     tipField : false,
43634     
43635     
43636     defaultAutoCreate : {
43637         tag: 'div',
43638         cls: 'x-cbarray-item',
43639         cn : [ 
43640             { tag: 'div' },
43641             {
43642                 tag: 'img',
43643                 width:16,
43644                 height : 16,
43645                 src : Roo.BLANK_IMAGE_URL ,
43646                 align: 'center'
43647             }
43648         ]
43649         
43650     },
43651     
43652  
43653     onRender : function(ct, position)
43654     {
43655         Roo.form.Field.superclass.onRender.call(this, ct, position);
43656         
43657         if(!this.el){
43658             var cfg = this.getAutoCreate();
43659             this.el = ct.createChild(cfg, position);
43660         }
43661         
43662         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43663         
43664         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43665             this.cb.renderer(this.data) :
43666             String.format('{0}',this.data[this.displayField]);
43667         
43668             
43669         this.el.child('div').dom.setAttribute('qtip',
43670                         String.format('{0}',this.data[this.tipField])
43671         );
43672         
43673         this.el.child('img').on('click', this.remove, this);
43674         
43675     },
43676    
43677     remove : function()
43678     {
43679         if(this.cb.disabled){
43680             return;
43681         }
43682         
43683         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43684             this.cb.items.remove(this);
43685             this.el.child('img').un('click', this.remove, this);
43686             this.el.remove();
43687             this.cb.updateHiddenEl();
43688
43689             this.cb.fireEvent('remove', this.cb, this);
43690         }
43691         
43692     }
43693 });/*
43694  * RooJS Library 1.1.1
43695  * Copyright(c) 2008-2011  Alan Knowles
43696  *
43697  * License - LGPL
43698  */
43699  
43700
43701 /**
43702  * @class Roo.form.ComboNested
43703  * @extends Roo.form.ComboBox
43704  * A combobox for that allows selection of nested items in a list,
43705  * eg.
43706  *
43707  *  Book
43708  *    -> red
43709  *    -> green
43710  *  Table
43711  *    -> square
43712  *      ->red
43713  *      ->green
43714  *    -> rectangle
43715  *      ->green
43716  *      
43717  * 
43718  * @constructor
43719  * Create a new ComboNested
43720  * @param {Object} config Configuration options
43721  */
43722 Roo.form.ComboNested = function(config){
43723     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43724     // should verify some data...
43725     // like
43726     // hiddenName = required..
43727     // displayField = required
43728     // valudField == required
43729     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43730     var _t = this;
43731     Roo.each(req, function(e) {
43732         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43733             throw "Roo.form.ComboNested : missing value for: " + e;
43734         }
43735     });
43736      
43737     
43738 };
43739
43740 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43741    
43742     /*
43743      * @config {Number} max Number of columns to show
43744      */
43745     
43746     maxColumns : 3,
43747    
43748     list : null, // the outermost div..
43749     innerLists : null, // the
43750     views : null,
43751     stores : null,
43752     // private
43753     loadingChildren : false,
43754     
43755     onRender : function(ct, position)
43756     {
43757         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43758         
43759         if(this.hiddenName){
43760             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43761                     'before', true);
43762             this.hiddenField.value =
43763                 this.hiddenValue !== undefined ? this.hiddenValue :
43764                 this.value !== undefined ? this.value : '';
43765
43766             // prevent input submission
43767             this.el.dom.removeAttribute('name');
43768              
43769              
43770         }
43771         
43772         if(Roo.isGecko){
43773             this.el.dom.setAttribute('autocomplete', 'off');
43774         }
43775
43776         var cls = 'x-combo-list';
43777
43778         this.list = new Roo.Layer({
43779             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43780         });
43781
43782         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43783         this.list.setWidth(lw);
43784         this.list.swallowEvent('mousewheel');
43785         this.assetHeight = 0;
43786
43787         if(this.title){
43788             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43789             this.assetHeight += this.header.getHeight();
43790         }
43791         this.innerLists = [];
43792         this.views = [];
43793         this.stores = [];
43794         for (var i =0 ; i < this.maxColumns; i++) {
43795             this.onRenderList( cls, i);
43796         }
43797         
43798         // always needs footer, as we are going to have an 'OK' button.
43799         this.footer = this.list.createChild({cls:cls+'-ft'});
43800         this.pageTb = new Roo.Toolbar(this.footer);  
43801         var _this = this;
43802         this.pageTb.add(  {
43803             
43804             text: 'Done',
43805             handler: function()
43806             {
43807                 _this.collapse();
43808             }
43809         });
43810         
43811         if ( this.allowBlank && !this.disableClear) {
43812             
43813             this.pageTb.add(new Roo.Toolbar.Fill(), {
43814                 cls: 'x-btn-icon x-btn-clear',
43815                 text: '&#160;',
43816                 handler: function()
43817                 {
43818                     _this.collapse();
43819                     _this.clearValue();
43820                     _this.onSelect(false, -1);
43821                 }
43822             });
43823         }
43824         if (this.footer) {
43825             this.assetHeight += this.footer.getHeight();
43826         }
43827         
43828     },
43829     onRenderList : function (  cls, i)
43830     {
43831         
43832         var lw = Math.floor(
43833                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43834         );
43835         
43836         this.list.setWidth(lw); // default to '1'
43837
43838         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43839         //il.on('mouseover', this.onViewOver, this, { list:  i });
43840         //il.on('mousemove', this.onViewMove, this, { list:  i });
43841         il.setWidth(lw);
43842         il.setStyle({ 'overflow-x' : 'hidden'});
43843
43844         if(!this.tpl){
43845             this.tpl = new Roo.Template({
43846                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43847                 isEmpty: function (value, allValues) {
43848                     //Roo.log(value);
43849                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43850                     return dl ? 'has-children' : 'no-children'
43851                 }
43852             });
43853         }
43854         
43855         var store  = this.store;
43856         if (i > 0) {
43857             store  = new Roo.data.SimpleStore({
43858                 //fields : this.store.reader.meta.fields,
43859                 reader : this.store.reader,
43860                 data : [ ]
43861             });
43862         }
43863         this.stores[i]  = store;
43864                   
43865         var view = this.views[i] = new Roo.View(
43866             il,
43867             this.tpl,
43868             {
43869                 singleSelect:true,
43870                 store: store,
43871                 selectedClass: this.selectedClass
43872             }
43873         );
43874         view.getEl().setWidth(lw);
43875         view.getEl().setStyle({
43876             position: i < 1 ? 'relative' : 'absolute',
43877             top: 0,
43878             left: (i * lw ) + 'px',
43879             display : i > 0 ? 'none' : 'block'
43880         });
43881         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43882         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43883         //view.on('click', this.onViewClick, this, { list : i });
43884
43885         store.on('beforeload', this.onBeforeLoad, this);
43886         store.on('load',  this.onLoad, this, { list  : i});
43887         store.on('loadexception', this.onLoadException, this);
43888
43889         // hide the other vies..
43890         
43891         
43892         
43893     },
43894       
43895     restrictHeight : function()
43896     {
43897         var mh = 0;
43898         Roo.each(this.innerLists, function(il,i) {
43899             var el = this.views[i].getEl();
43900             el.dom.style.height = '';
43901             var inner = el.dom;
43902             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43903             // only adjust heights on other ones..
43904             mh = Math.max(h, mh);
43905             if (i < 1) {
43906                 
43907                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43908                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43909                
43910             }
43911             
43912             
43913         }, this);
43914         
43915         this.list.beginUpdate();
43916         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43917         this.list.alignTo(this.el, this.listAlign);
43918         this.list.endUpdate();
43919         
43920     },
43921      
43922     
43923     // -- store handlers..
43924     // private
43925     onBeforeLoad : function()
43926     {
43927         if(!this.hasFocus){
43928             return;
43929         }
43930         this.innerLists[0].update(this.loadingText ?
43931                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43932         this.restrictHeight();
43933         this.selectedIndex = -1;
43934     },
43935     // private
43936     onLoad : function(a,b,c,d)
43937     {
43938         if (!this.loadingChildren) {
43939             // then we are loading the top level. - hide the children
43940             for (var i = 1;i < this.views.length; i++) {
43941                 this.views[i].getEl().setStyle({ display : 'none' });
43942             }
43943             var lw = Math.floor(
43944                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43945             );
43946         
43947              this.list.setWidth(lw); // default to '1'
43948
43949             
43950         }
43951         if(!this.hasFocus){
43952             return;
43953         }
43954         
43955         if(this.store.getCount() > 0) {
43956             this.expand();
43957             this.restrictHeight();   
43958         } else {
43959             this.onEmptyResults();
43960         }
43961         
43962         if (!this.loadingChildren) {
43963             this.selectActive();
43964         }
43965         /*
43966         this.stores[1].loadData([]);
43967         this.stores[2].loadData([]);
43968         this.views
43969         */    
43970     
43971         //this.el.focus();
43972     },
43973     
43974     
43975     // private
43976     onLoadException : function()
43977     {
43978         this.collapse();
43979         Roo.log(this.store.reader.jsonData);
43980         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43981             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43982         }
43983         
43984         
43985     },
43986     // no cleaning of leading spaces on blur here.
43987     cleanLeadingSpace : function(e) { },
43988     
43989
43990     onSelectChange : function (view, sels, opts )
43991     {
43992         var ix = view.getSelectedIndexes();
43993          
43994         if (opts.list > this.maxColumns - 2) {
43995             if (view.store.getCount()<  1) {
43996                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43997
43998             } else  {
43999                 if (ix.length) {
44000                     // used to clear ?? but if we are loading unselected 
44001                     this.setFromData(view.store.getAt(ix[0]).data);
44002                 }
44003                 
44004             }
44005             
44006             return;
44007         }
44008         
44009         if (!ix.length) {
44010             // this get's fired when trigger opens..
44011            // this.setFromData({});
44012             var str = this.stores[opts.list+1];
44013             str.data.clear(); // removeall wihtout the fire events..
44014             return;
44015         }
44016         
44017         var rec = view.store.getAt(ix[0]);
44018          
44019         this.setFromData(rec.data);
44020         this.fireEvent('select', this, rec, ix[0]);
44021         
44022         var lw = Math.floor(
44023              (
44024                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44025              ) / this.maxColumns
44026         );
44027         this.loadingChildren = true;
44028         this.stores[opts.list+1].loadDataFromChildren( rec );
44029         this.loadingChildren = false;
44030         var dl = this.stores[opts.list+1]. getTotalCount();
44031         
44032         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44033         
44034         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44035         for (var i = opts.list+2; i < this.views.length;i++) {
44036             this.views[i].getEl().setStyle({ display : 'none' });
44037         }
44038         
44039         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44040         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44041         
44042         if (this.isLoading) {
44043            // this.selectActive(opts.list);
44044         }
44045          
44046     },
44047     
44048     
44049     
44050     
44051     onDoubleClick : function()
44052     {
44053         this.collapse(); //??
44054     },
44055     
44056      
44057     
44058     
44059     
44060     // private
44061     recordToStack : function(store, prop, value, stack)
44062     {
44063         var cstore = new Roo.data.SimpleStore({
44064             //fields : this.store.reader.meta.fields, // we need array reader.. for
44065             reader : this.store.reader,
44066             data : [ ]
44067         });
44068         var _this = this;
44069         var record  = false;
44070         var srec = false;
44071         if(store.getCount() < 1){
44072             return false;
44073         }
44074         store.each(function(r){
44075             if(r.data[prop] == value){
44076                 record = r;
44077             srec = r;
44078                 return false;
44079             }
44080             if (r.data.cn && r.data.cn.length) {
44081                 cstore.loadDataFromChildren( r);
44082                 var cret = _this.recordToStack(cstore, prop, value, stack);
44083                 if (cret !== false) {
44084                     record = cret;
44085                     srec = r;
44086                     return false;
44087                 }
44088             }
44089              
44090             return true;
44091         });
44092         if (record == false) {
44093             return false
44094         }
44095         stack.unshift(srec);
44096         return record;
44097     },
44098     
44099     /*
44100      * find the stack of stores that match our value.
44101      *
44102      * 
44103      */
44104     
44105     selectActive : function ()
44106     {
44107         // if store is not loaded, then we will need to wait for that to happen first.
44108         var stack = [];
44109         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44110         for (var i = 0; i < stack.length; i++ ) {
44111             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44112         }
44113         
44114     }
44115         
44116          
44117     
44118     
44119     
44120     
44121 });/*
44122  * Based on:
44123  * Ext JS Library 1.1.1
44124  * Copyright(c) 2006-2007, Ext JS, LLC.
44125  *
44126  * Originally Released Under LGPL - original licence link has changed is not relivant.
44127  *
44128  * Fork - LGPL
44129  * <script type="text/javascript">
44130  */
44131 /**
44132  * @class Roo.form.Checkbox
44133  * @extends Roo.form.Field
44134  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44135  * @constructor
44136  * Creates a new Checkbox
44137  * @param {Object} config Configuration options
44138  */
44139 Roo.form.Checkbox = function(config){
44140     Roo.form.Checkbox.superclass.constructor.call(this, config);
44141     this.addEvents({
44142         /**
44143          * @event check
44144          * Fires when the checkbox is checked or unchecked.
44145              * @param {Roo.form.Checkbox} this This checkbox
44146              * @param {Boolean} checked The new checked value
44147              */
44148         check : true
44149     });
44150 };
44151
44152 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44153     /**
44154      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44155      */
44156     focusClass : undefined,
44157     /**
44158      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44159      */
44160     fieldClass: "x-form-field",
44161     /**
44162      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44163      */
44164     checked: false,
44165     /**
44166      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44167      * {tag: "input", type: "checkbox", autocomplete: "off"})
44168      */
44169     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44170     /**
44171      * @cfg {String} boxLabel The text that appears beside the checkbox
44172      */
44173     boxLabel : "",
44174     /**
44175      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44176      */  
44177     inputValue : '1',
44178     /**
44179      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44180      */
44181      valueOff: '0', // value when not checked..
44182
44183     actionMode : 'viewEl', 
44184     //
44185     // private
44186     itemCls : 'x-menu-check-item x-form-item',
44187     groupClass : 'x-menu-group-item',
44188     inputType : 'hidden',
44189     
44190     
44191     inSetChecked: false, // check that we are not calling self...
44192     
44193     inputElement: false, // real input element?
44194     basedOn: false, // ????
44195     
44196     isFormField: true, // not sure where this is needed!!!!
44197
44198     onResize : function(){
44199         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44200         if(!this.boxLabel){
44201             this.el.alignTo(this.wrap, 'c-c');
44202         }
44203     },
44204
44205     initEvents : function(){
44206         Roo.form.Checkbox.superclass.initEvents.call(this);
44207         this.el.on("click", this.onClick,  this);
44208         this.el.on("change", this.onClick,  this);
44209     },
44210
44211
44212     getResizeEl : function(){
44213         return this.wrap;
44214     },
44215
44216     getPositionEl : function(){
44217         return this.wrap;
44218     },
44219
44220     // private
44221     onRender : function(ct, position){
44222         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44223         /*
44224         if(this.inputValue !== undefined){
44225             this.el.dom.value = this.inputValue;
44226         }
44227         */
44228         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44229         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44230         var viewEl = this.wrap.createChild({ 
44231             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44232         this.viewEl = viewEl;   
44233         this.wrap.on('click', this.onClick,  this); 
44234         
44235         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44236         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44237         
44238         
44239         
44240         if(this.boxLabel){
44241             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44242         //    viewEl.on('click', this.onClick,  this); 
44243         }
44244         //if(this.checked){
44245             this.setChecked(this.checked);
44246         //}else{
44247             //this.checked = this.el.dom;
44248         //}
44249
44250     },
44251
44252     // private
44253     initValue : Roo.emptyFn,
44254
44255     /**
44256      * Returns the checked state of the checkbox.
44257      * @return {Boolean} True if checked, else false
44258      */
44259     getValue : function(){
44260         if(this.el){
44261             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44262         }
44263         return this.valueOff;
44264         
44265     },
44266
44267         // private
44268     onClick : function(){ 
44269         if (this.disabled) {
44270             return;
44271         }
44272         this.setChecked(!this.checked);
44273
44274         //if(this.el.dom.checked != this.checked){
44275         //    this.setValue(this.el.dom.checked);
44276        // }
44277     },
44278
44279     /**
44280      * Sets the checked state of the checkbox.
44281      * On is always based on a string comparison between inputValue and the param.
44282      * @param {Boolean/String} value - the value to set 
44283      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44284      */
44285     setValue : function(v,suppressEvent){
44286         
44287         
44288         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44289         //if(this.el && this.el.dom){
44290         //    this.el.dom.checked = this.checked;
44291         //    this.el.dom.defaultChecked = this.checked;
44292         //}
44293         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44294         //this.fireEvent("check", this, this.checked);
44295     },
44296     // private..
44297     setChecked : function(state,suppressEvent)
44298     {
44299         if (this.inSetChecked) {
44300             this.checked = state;
44301             return;
44302         }
44303         
44304     
44305         if(this.wrap){
44306             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44307         }
44308         this.checked = state;
44309         if(suppressEvent !== true){
44310             this.fireEvent('check', this, state);
44311         }
44312         this.inSetChecked = true;
44313         this.el.dom.value = state ? this.inputValue : this.valueOff;
44314         this.inSetChecked = false;
44315         
44316     },
44317     // handle setting of hidden value by some other method!!?!?
44318     setFromHidden: function()
44319     {
44320         if(!this.el){
44321             return;
44322         }
44323         //console.log("SET FROM HIDDEN");
44324         //alert('setFrom hidden');
44325         this.setValue(this.el.dom.value);
44326     },
44327     
44328     onDestroy : function()
44329     {
44330         if(this.viewEl){
44331             Roo.get(this.viewEl).remove();
44332         }
44333          
44334         Roo.form.Checkbox.superclass.onDestroy.call(this);
44335     },
44336     
44337     setBoxLabel : function(str)
44338     {
44339         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44340     }
44341
44342 });/*
44343  * Based on:
44344  * Ext JS Library 1.1.1
44345  * Copyright(c) 2006-2007, Ext JS, LLC.
44346  *
44347  * Originally Released Under LGPL - original licence link has changed is not relivant.
44348  *
44349  * Fork - LGPL
44350  * <script type="text/javascript">
44351  */
44352  
44353 /**
44354  * @class Roo.form.Radio
44355  * @extends Roo.form.Checkbox
44356  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44357  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44358  * @constructor
44359  * Creates a new Radio
44360  * @param {Object} config Configuration options
44361  */
44362 Roo.form.Radio = function(){
44363     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44364 };
44365 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44366     inputType: 'radio',
44367
44368     /**
44369      * If this radio is part of a group, it will return the selected value
44370      * @return {String}
44371      */
44372     getGroupValue : function(){
44373         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44374     },
44375     
44376     
44377     onRender : function(ct, position){
44378         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44379         
44380         if(this.inputValue !== undefined){
44381             this.el.dom.value = this.inputValue;
44382         }
44383          
44384         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44385         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44386         //var viewEl = this.wrap.createChild({ 
44387         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44388         //this.viewEl = viewEl;   
44389         //this.wrap.on('click', this.onClick,  this); 
44390         
44391         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44392         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44393         
44394         
44395         
44396         if(this.boxLabel){
44397             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44398         //    viewEl.on('click', this.onClick,  this); 
44399         }
44400          if(this.checked){
44401             this.el.dom.checked =   'checked' ;
44402         }
44403          
44404     } 
44405     
44406     
44407 });//<script type="text/javascript">
44408
44409 /*
44410  * Based  Ext JS Library 1.1.1
44411  * Copyright(c) 2006-2007, Ext JS, LLC.
44412  * LGPL
44413  *
44414  */
44415  
44416 /**
44417  * @class Roo.HtmlEditorCore
44418  * @extends Roo.Component
44419  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
44420  *
44421  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44422  */
44423
44424 Roo.HtmlEditorCore = function(config){
44425     
44426     
44427     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
44428     
44429     
44430     this.addEvents({
44431         /**
44432          * @event initialize
44433          * Fires when the editor is fully initialized (including the iframe)
44434          * @param {Roo.HtmlEditorCore} this
44435          */
44436         initialize: true,
44437         /**
44438          * @event activate
44439          * Fires when the editor is first receives the focus. Any insertion must wait
44440          * until after this event.
44441          * @param {Roo.HtmlEditorCore} this
44442          */
44443         activate: true,
44444          /**
44445          * @event beforesync
44446          * Fires before the textarea is updated with content from the editor iframe. Return false
44447          * to cancel the sync.
44448          * @param {Roo.HtmlEditorCore} this
44449          * @param {String} html
44450          */
44451         beforesync: true,
44452          /**
44453          * @event beforepush
44454          * Fires before the iframe editor is updated with content from the textarea. Return false
44455          * to cancel the push.
44456          * @param {Roo.HtmlEditorCore} this
44457          * @param {String} html
44458          */
44459         beforepush: true,
44460          /**
44461          * @event sync
44462          * Fires when the textarea is updated with content from the editor iframe.
44463          * @param {Roo.HtmlEditorCore} this
44464          * @param {String} html
44465          */
44466         sync: true,
44467          /**
44468          * @event push
44469          * Fires when the iframe editor is updated with content from the textarea.
44470          * @param {Roo.HtmlEditorCore} this
44471          * @param {String} html
44472          */
44473         push: true,
44474         
44475         /**
44476          * @event editorevent
44477          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44478          * @param {Roo.HtmlEditorCore} this
44479          */
44480         editorevent: true
44481         
44482     });
44483     
44484     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
44485     
44486     // defaults : white / black...
44487     this.applyBlacklists();
44488     
44489     
44490     
44491 };
44492
44493
44494 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
44495
44496
44497      /**
44498      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
44499      */
44500     
44501     owner : false,
44502     
44503      /**
44504      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44505      *                        Roo.resizable.
44506      */
44507     resizable : false,
44508      /**
44509      * @cfg {Number} height (in pixels)
44510      */   
44511     height: 300,
44512    /**
44513      * @cfg {Number} width (in pixels)
44514      */   
44515     width: 500,
44516     
44517     /**
44518      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44519      * 
44520      */
44521     stylesheets: false,
44522     
44523     /**
44524      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
44525      */
44526     allowComments: false,
44527     // id of frame..
44528     frameId: false,
44529     
44530     // private properties
44531     validationEvent : false,
44532     deferHeight: true,
44533     initialized : false,
44534     activated : false,
44535     sourceEditMode : false,
44536     onFocus : Roo.emptyFn,
44537     iframePad:3,
44538     hideMode:'offsets',
44539     
44540     clearUp: true,
44541     
44542     // blacklist + whitelisted elements..
44543     black: false,
44544     white: false,
44545      
44546     bodyCls : '',
44547
44548     /**
44549      * Protected method that will not generally be called directly. It
44550      * is called when the editor initializes the iframe with HTML contents. Override this method if you
44551      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
44552      */
44553     getDocMarkup : function(){
44554         // body styles..
44555         var st = '';
44556         
44557         // inherit styels from page...?? 
44558         if (this.stylesheets === false) {
44559             
44560             Roo.get(document.head).select('style').each(function(node) {
44561                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44562             });
44563             
44564             Roo.get(document.head).select('link').each(function(node) { 
44565                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44566             });
44567             
44568         } else if (!this.stylesheets.length) {
44569                 // simple..
44570                 st = '<style type="text/css">' +
44571                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44572                    '</style>';
44573         } else {
44574             for (var i in this.stylesheets) { 
44575                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
44576             }
44577             
44578         }
44579         
44580         st +=  '<style type="text/css">' +
44581             'IMG { cursor: pointer } ' +
44582         '</style>';
44583
44584         var cls = 'roo-htmleditor-body';
44585         
44586         if(this.bodyCls.length){
44587             cls += ' ' + this.bodyCls;
44588         }
44589         
44590         return '<html><head>' + st  +
44591             //<style type="text/css">' +
44592             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44593             //'</style>' +
44594             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
44595     },
44596
44597     // private
44598     onRender : function(ct, position)
44599     {
44600         var _t = this;
44601         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
44602         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
44603         
44604         
44605         this.el.dom.style.border = '0 none';
44606         this.el.dom.setAttribute('tabIndex', -1);
44607         this.el.addClass('x-hidden hide');
44608         
44609         
44610         
44611         if(Roo.isIE){ // fix IE 1px bogus margin
44612             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
44613         }
44614        
44615         
44616         this.frameId = Roo.id();
44617         
44618          
44619         
44620         var iframe = this.owner.wrap.createChild({
44621             tag: 'iframe',
44622             cls: 'form-control', // bootstrap..
44623             id: this.frameId,
44624             name: this.frameId,
44625             frameBorder : 'no',
44626             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
44627         }, this.el
44628         );
44629         
44630         
44631         this.iframe = iframe.dom;
44632
44633          this.assignDocWin();
44634         
44635         this.doc.designMode = 'on';
44636        
44637         this.doc.open();
44638         this.doc.write(this.getDocMarkup());
44639         this.doc.close();
44640
44641         
44642         var task = { // must defer to wait for browser to be ready
44643             run : function(){
44644                 //console.log("run task?" + this.doc.readyState);
44645                 this.assignDocWin();
44646                 if(this.doc.body || this.doc.readyState == 'complete'){
44647                     try {
44648                         this.doc.designMode="on";
44649                     } catch (e) {
44650                         return;
44651                     }
44652                     Roo.TaskMgr.stop(task);
44653                     this.initEditor.defer(10, this);
44654                 }
44655             },
44656             interval : 10,
44657             duration: 10000,
44658             scope: this
44659         };
44660         Roo.TaskMgr.start(task);
44661
44662     },
44663
44664     // private
44665     onResize : function(w, h)
44666     {
44667          Roo.log('resize: ' +w + ',' + h );
44668         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
44669         if(!this.iframe){
44670             return;
44671         }
44672         if(typeof w == 'number'){
44673             
44674             this.iframe.style.width = w + 'px';
44675         }
44676         if(typeof h == 'number'){
44677             
44678             this.iframe.style.height = h + 'px';
44679             if(this.doc){
44680                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
44681             }
44682         }
44683         
44684     },
44685
44686     /**
44687      * Toggles the editor between standard and source edit mode.
44688      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44689      */
44690     toggleSourceEdit : function(sourceEditMode){
44691         
44692         this.sourceEditMode = sourceEditMode === true;
44693         
44694         if(this.sourceEditMode){
44695  
44696             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
44697             
44698         }else{
44699             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
44700             //this.iframe.className = '';
44701             this.deferFocus();
44702         }
44703         //this.setSize(this.owner.wrap.getSize());
44704         //this.fireEvent('editmodechange', this, this.sourceEditMode);
44705     },
44706
44707     
44708   
44709
44710     /**
44711      * Protected method that will not generally be called directly. If you need/want
44712      * custom HTML cleanup, this is the method you should override.
44713      * @param {String} html The HTML to be cleaned
44714      * return {String} The cleaned HTML
44715      */
44716     cleanHtml : function(html){
44717         html = String(html);
44718         if(html.length > 5){
44719             if(Roo.isSafari){ // strip safari nonsense
44720                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44721             }
44722         }
44723         if(html == '&nbsp;'){
44724             html = '';
44725         }
44726         return html;
44727     },
44728
44729     /**
44730      * HTML Editor -> Textarea
44731      * Protected method that will not generally be called directly. Syncs the contents
44732      * of the editor iframe with the textarea.
44733      */
44734     syncValue : function(){
44735         if(this.initialized){
44736             var bd = (this.doc.body || this.doc.documentElement);
44737             //this.cleanUpPaste(); -- this is done else where and causes havoc..
44738             var html = bd.innerHTML;
44739             if(Roo.isSafari){
44740                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44741                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44742                 if(m && m[1]){
44743                     html = '<div style="'+m[0]+'">' + html + '</div>';
44744                 }
44745             }
44746             html = this.cleanHtml(html);
44747             // fix up the special chars.. normaly like back quotes in word...
44748             // however we do not want to do this with chinese..
44749             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
44750                 
44751                 var cc = match.charCodeAt();
44752
44753                 // Get the character value, handling surrogate pairs
44754                 if (match.length == 2) {
44755                     // It's a surrogate pair, calculate the Unicode code point
44756                     var high = match.charCodeAt(0) - 0xD800;
44757                     var low  = match.charCodeAt(1) - 0xDC00;
44758                     cc = (high * 0x400) + low + 0x10000;
44759                 }  else if (
44760                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44761                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44762                     (cc >= 0xf900 && cc < 0xfb00 )
44763                 ) {
44764                         return match;
44765                 }  
44766          
44767                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44768                 return "&#" + cc + ";";
44769                 
44770                 
44771             });
44772             
44773             
44774              
44775             if(this.owner.fireEvent('beforesync', this, html) !== false){
44776                 this.el.dom.value = html;
44777                 this.owner.fireEvent('sync', this, html);
44778             }
44779         }
44780     },
44781
44782     /**
44783      * Protected method that will not generally be called directly. Pushes the value of the textarea
44784      * into the iframe editor.
44785      */
44786     pushValue : function(){
44787         if(this.initialized){
44788             var v = this.el.dom.value.trim();
44789             
44790 //            if(v.length < 1){
44791 //                v = '&#160;';
44792 //            }
44793             
44794             if(this.owner.fireEvent('beforepush', this, v) !== false){
44795                 var d = (this.doc.body || this.doc.documentElement);
44796                 d.innerHTML = v;
44797                 this.cleanUpPaste();
44798                 this.el.dom.value = d.innerHTML;
44799                 this.owner.fireEvent('push', this, v);
44800             }
44801         }
44802     },
44803
44804     // private
44805     deferFocus : function(){
44806         this.focus.defer(10, this);
44807     },
44808
44809     // doc'ed in Field
44810     focus : function(){
44811         if(this.win && !this.sourceEditMode){
44812             this.win.focus();
44813         }else{
44814             this.el.focus();
44815         }
44816     },
44817     
44818     assignDocWin: function()
44819     {
44820         var iframe = this.iframe;
44821         
44822          if(Roo.isIE){
44823             this.doc = iframe.contentWindow.document;
44824             this.win = iframe.contentWindow;
44825         } else {
44826 //            if (!Roo.get(this.frameId)) {
44827 //                return;
44828 //            }
44829 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44830 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44831             
44832             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44833                 return;
44834             }
44835             
44836             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44837             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44838         }
44839     },
44840     
44841     // private
44842     initEditor : function(){
44843         //console.log("INIT EDITOR");
44844         this.assignDocWin();
44845         
44846         
44847         
44848         this.doc.designMode="on";
44849         this.doc.open();
44850         this.doc.write(this.getDocMarkup());
44851         this.doc.close();
44852         
44853         var dbody = (this.doc.body || this.doc.documentElement);
44854         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44855         // this copies styles from the containing element into thsi one..
44856         // not sure why we need all of this..
44857         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44858         
44859         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44860         //ss['background-attachment'] = 'fixed'; // w3c
44861         dbody.bgProperties = 'fixed'; // ie
44862         //Roo.DomHelper.applyStyles(dbody, ss);
44863         Roo.EventManager.on(this.doc, {
44864             //'mousedown': this.onEditorEvent,
44865             'mouseup': this.onEditorEvent,
44866             'dblclick': this.onEditorEvent,
44867             'click': this.onEditorEvent,
44868             'keyup': this.onEditorEvent,
44869             buffer:100,
44870             scope: this
44871         });
44872         if(Roo.isGecko){
44873             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44874         }
44875         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44876             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44877         }
44878         this.initialized = true;
44879
44880         this.owner.fireEvent('initialize', this);
44881         this.pushValue();
44882     },
44883
44884     // private
44885     onDestroy : function(){
44886         
44887         
44888         
44889         if(this.rendered){
44890             
44891             //for (var i =0; i < this.toolbars.length;i++) {
44892             //    // fixme - ask toolbars for heights?
44893             //    this.toolbars[i].onDestroy();
44894            // }
44895             
44896             //this.wrap.dom.innerHTML = '';
44897             //this.wrap.remove();
44898         }
44899     },
44900
44901     // private
44902     onFirstFocus : function(){
44903         
44904         this.assignDocWin();
44905         
44906         
44907         this.activated = true;
44908          
44909     
44910         if(Roo.isGecko){ // prevent silly gecko errors
44911             this.win.focus();
44912             var s = this.win.getSelection();
44913             if(!s.focusNode || s.focusNode.nodeType != 3){
44914                 var r = s.getRangeAt(0);
44915                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44916                 r.collapse(true);
44917                 this.deferFocus();
44918             }
44919             try{
44920                 this.execCmd('useCSS', true);
44921                 this.execCmd('styleWithCSS', false);
44922             }catch(e){}
44923         }
44924         this.owner.fireEvent('activate', this);
44925     },
44926
44927     // private
44928     adjustFont: function(btn){
44929         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44930         //if(Roo.isSafari){ // safari
44931         //    adjust *= 2;
44932        // }
44933         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44934         if(Roo.isSafari){ // safari
44935             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44936             v =  (v < 10) ? 10 : v;
44937             v =  (v > 48) ? 48 : v;
44938             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44939             
44940         }
44941         
44942         
44943         v = Math.max(1, v+adjust);
44944         
44945         this.execCmd('FontSize', v  );
44946     },
44947
44948     onEditorEvent : function(e)
44949     {
44950         this.owner.fireEvent('editorevent', this, e);
44951       //  this.updateToolbar();
44952         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44953     },
44954
44955     insertTag : function(tg)
44956     {
44957         // could be a bit smarter... -> wrap the current selected tRoo..
44958         if (tg.toLowerCase() == 'span' ||
44959             tg.toLowerCase() == 'code' ||
44960             tg.toLowerCase() == 'sup' ||
44961             tg.toLowerCase() == 'sub' 
44962             ) {
44963             
44964             range = this.createRange(this.getSelection());
44965             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44966             wrappingNode.appendChild(range.extractContents());
44967             range.insertNode(wrappingNode);
44968
44969             return;
44970             
44971             
44972             
44973         }
44974         this.execCmd("formatblock",   tg);
44975         
44976     },
44977     
44978     insertText : function(txt)
44979     {
44980         
44981         
44982         var range = this.createRange();
44983         range.deleteContents();
44984                //alert(Sender.getAttribute('label'));
44985                
44986         range.insertNode(this.doc.createTextNode(txt));
44987     } ,
44988     
44989      
44990
44991     /**
44992      * Executes a Midas editor command on the editor document and performs necessary focus and
44993      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44994      * @param {String} cmd The Midas command
44995      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44996      */
44997     relayCmd : function(cmd, value){
44998         this.win.focus();
44999         this.execCmd(cmd, value);
45000         this.owner.fireEvent('editorevent', this);
45001         //this.updateToolbar();
45002         this.owner.deferFocus();
45003     },
45004
45005     /**
45006      * Executes a Midas editor command directly on the editor document.
45007      * For visual commands, you should use {@link #relayCmd} instead.
45008      * <b>This should only be called after the editor is initialized.</b>
45009      * @param {String} cmd The Midas command
45010      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45011      */
45012     execCmd : function(cmd, value){
45013         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45014         this.syncValue();
45015     },
45016  
45017  
45018    
45019     /**
45020      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45021      * to insert tRoo.
45022      * @param {String} text | dom node.. 
45023      */
45024     insertAtCursor : function(text)
45025     {
45026         
45027         if(!this.activated){
45028             return;
45029         }
45030         /*
45031         if(Roo.isIE){
45032             this.win.focus();
45033             var r = this.doc.selection.createRange();
45034             if(r){
45035                 r.collapse(true);
45036                 r.pasteHTML(text);
45037                 this.syncValue();
45038                 this.deferFocus();
45039             
45040             }
45041             return;
45042         }
45043         */
45044         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
45045             this.win.focus();
45046             
45047             
45048             // from jquery ui (MIT licenced)
45049             var range, node;
45050             var win = this.win;
45051             
45052             if (win.getSelection && win.getSelection().getRangeAt) {
45053                 range = win.getSelection().getRangeAt(0);
45054                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
45055                 range.insertNode(node);
45056             } else if (win.document.selection && win.document.selection.createRange) {
45057                 // no firefox support
45058                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45059                 win.document.selection.createRange().pasteHTML(txt);
45060             } else {
45061                 // no firefox support
45062                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45063                 this.execCmd('InsertHTML', txt);
45064             } 
45065             
45066             this.syncValue();
45067             
45068             this.deferFocus();
45069         }
45070     },
45071  // private
45072     mozKeyPress : function(e){
45073         if(e.ctrlKey){
45074             var c = e.getCharCode(), cmd;
45075           
45076             if(c > 0){
45077                 c = String.fromCharCode(c).toLowerCase();
45078                 switch(c){
45079                     case 'b':
45080                         cmd = 'bold';
45081                         break;
45082                     case 'i':
45083                         cmd = 'italic';
45084                         break;
45085                     
45086                     case 'u':
45087                         cmd = 'underline';
45088                         break;
45089                     
45090                     case 'v':
45091                         this.cleanUpPaste.defer(100, this);
45092                         return;
45093                         
45094                 }
45095                 if(cmd){
45096                     this.win.focus();
45097                     this.execCmd(cmd);
45098                     this.deferFocus();
45099                     e.preventDefault();
45100                 }
45101                 
45102             }
45103         }
45104     },
45105
45106     // private
45107     fixKeys : function(){ // load time branching for fastest keydown performance
45108         if(Roo.isIE){
45109             return function(e){
45110                 var k = e.getKey(), r;
45111                 if(k == e.TAB){
45112                     e.stopEvent();
45113                     r = this.doc.selection.createRange();
45114                     if(r){
45115                         r.collapse(true);
45116                         r.pasteHTML('&#160;&#160;&#160;&#160;');
45117                         this.deferFocus();
45118                     }
45119                     return;
45120                 }
45121                 
45122                 if(k == e.ENTER){
45123                     r = this.doc.selection.createRange();
45124                     if(r){
45125                         var target = r.parentElement();
45126                         if(!target || target.tagName.toLowerCase() != 'li'){
45127                             e.stopEvent();
45128                             r.pasteHTML('<br />');
45129                             r.collapse(false);
45130                             r.select();
45131                         }
45132                     }
45133                 }
45134                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45135                     this.cleanUpPaste.defer(100, this);
45136                     return;
45137                 }
45138                 
45139                 
45140             };
45141         }else if(Roo.isOpera){
45142             return function(e){
45143                 var k = e.getKey();
45144                 if(k == e.TAB){
45145                     e.stopEvent();
45146                     this.win.focus();
45147                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
45148                     this.deferFocus();
45149                 }
45150                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45151                     this.cleanUpPaste.defer(100, this);
45152                     return;
45153                 }
45154                 
45155             };
45156         }else if(Roo.isSafari){
45157             return function(e){
45158                 var k = e.getKey();
45159                 
45160                 if(k == e.TAB){
45161                     e.stopEvent();
45162                     this.execCmd('InsertText','\t');
45163                     this.deferFocus();
45164                     return;
45165                 }
45166                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45167                     this.cleanUpPaste.defer(100, this);
45168                     return;
45169                 }
45170                 
45171              };
45172         }
45173     }(),
45174     
45175     getAllAncestors: function()
45176     {
45177         var p = this.getSelectedNode();
45178         var a = [];
45179         if (!p) {
45180             a.push(p); // push blank onto stack..
45181             p = this.getParentElement();
45182         }
45183         
45184         
45185         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
45186             a.push(p);
45187             p = p.parentNode;
45188         }
45189         a.push(this.doc.body);
45190         return a;
45191     },
45192     lastSel : false,
45193     lastSelNode : false,
45194     
45195     
45196     getSelection : function() 
45197     {
45198         this.assignDocWin();
45199         return Roo.isIE ? this.doc.selection : this.win.getSelection();
45200     },
45201     
45202     getSelectedNode: function() 
45203     {
45204         // this may only work on Gecko!!!
45205         
45206         // should we cache this!!!!
45207         
45208         
45209         
45210          
45211         var range = this.createRange(this.getSelection()).cloneRange();
45212         
45213         if (Roo.isIE) {
45214             var parent = range.parentElement();
45215             while (true) {
45216                 var testRange = range.duplicate();
45217                 testRange.moveToElementText(parent);
45218                 if (testRange.inRange(range)) {
45219                     break;
45220                 }
45221                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
45222                     break;
45223                 }
45224                 parent = parent.parentElement;
45225             }
45226             return parent;
45227         }
45228         
45229         // is ancestor a text element.
45230         var ac =  range.commonAncestorContainer;
45231         if (ac.nodeType == 3) {
45232             ac = ac.parentNode;
45233         }
45234         
45235         var ar = ac.childNodes;
45236          
45237         var nodes = [];
45238         var other_nodes = [];
45239         var has_other_nodes = false;
45240         for (var i=0;i<ar.length;i++) {
45241             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
45242                 continue;
45243             }
45244             // fullly contained node.
45245             
45246             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
45247                 nodes.push(ar[i]);
45248                 continue;
45249             }
45250             
45251             // probably selected..
45252             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
45253                 other_nodes.push(ar[i]);
45254                 continue;
45255             }
45256             // outer..
45257             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
45258                 continue;
45259             }
45260             
45261             
45262             has_other_nodes = true;
45263         }
45264         if (!nodes.length && other_nodes.length) {
45265             nodes= other_nodes;
45266         }
45267         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
45268             return false;
45269         }
45270         
45271         return nodes[0];
45272     },
45273     createRange: function(sel)
45274     {
45275         // this has strange effects when using with 
45276         // top toolbar - not sure if it's a great idea.
45277         //this.editor.contentWindow.focus();
45278         if (typeof sel != "undefined") {
45279             try {
45280                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
45281             } catch(e) {
45282                 return this.doc.createRange();
45283             }
45284         } else {
45285             return this.doc.createRange();
45286         }
45287     },
45288     getParentElement: function()
45289     {
45290         
45291         this.assignDocWin();
45292         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
45293         
45294         var range = this.createRange(sel);
45295          
45296         try {
45297             var p = range.commonAncestorContainer;
45298             while (p.nodeType == 3) { // text node
45299                 p = p.parentNode;
45300             }
45301             return p;
45302         } catch (e) {
45303             return null;
45304         }
45305     
45306     },
45307     /***
45308      *
45309      * Range intersection.. the hard stuff...
45310      *  '-1' = before
45311      *  '0' = hits..
45312      *  '1' = after.
45313      *         [ -- selected range --- ]
45314      *   [fail]                        [fail]
45315      *
45316      *    basically..
45317      *      if end is before start or  hits it. fail.
45318      *      if start is after end or hits it fail.
45319      *
45320      *   if either hits (but other is outside. - then it's not 
45321      *   
45322      *    
45323      **/
45324     
45325     
45326     // @see http://www.thismuchiknow.co.uk/?p=64.
45327     rangeIntersectsNode : function(range, node)
45328     {
45329         var nodeRange = node.ownerDocument.createRange();
45330         try {
45331             nodeRange.selectNode(node);
45332         } catch (e) {
45333             nodeRange.selectNodeContents(node);
45334         }
45335     
45336         var rangeStartRange = range.cloneRange();
45337         rangeStartRange.collapse(true);
45338     
45339         var rangeEndRange = range.cloneRange();
45340         rangeEndRange.collapse(false);
45341     
45342         var nodeStartRange = nodeRange.cloneRange();
45343         nodeStartRange.collapse(true);
45344     
45345         var nodeEndRange = nodeRange.cloneRange();
45346         nodeEndRange.collapse(false);
45347     
45348         return rangeStartRange.compareBoundaryPoints(
45349                  Range.START_TO_START, nodeEndRange) == -1 &&
45350                rangeEndRange.compareBoundaryPoints(
45351                  Range.START_TO_START, nodeStartRange) == 1;
45352         
45353          
45354     },
45355     rangeCompareNode : function(range, node)
45356     {
45357         var nodeRange = node.ownerDocument.createRange();
45358         try {
45359             nodeRange.selectNode(node);
45360         } catch (e) {
45361             nodeRange.selectNodeContents(node);
45362         }
45363         
45364         
45365         range.collapse(true);
45366     
45367         nodeRange.collapse(true);
45368      
45369         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
45370         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
45371          
45372         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
45373         
45374         var nodeIsBefore   =  ss == 1;
45375         var nodeIsAfter    = ee == -1;
45376         
45377         if (nodeIsBefore && nodeIsAfter) {
45378             return 0; // outer
45379         }
45380         if (!nodeIsBefore && nodeIsAfter) {
45381             return 1; //right trailed.
45382         }
45383         
45384         if (nodeIsBefore && !nodeIsAfter) {
45385             return 2;  // left trailed.
45386         }
45387         // fully contined.
45388         return 3;
45389     },
45390
45391     // private? - in a new class?
45392     cleanUpPaste :  function()
45393     {
45394         // cleans up the whole document..
45395         Roo.log('cleanuppaste');
45396         
45397         this.cleanUpChildren(this.doc.body);
45398         var clean = this.cleanWordChars(this.doc.body.innerHTML);
45399         if (clean != this.doc.body.innerHTML) {
45400             this.doc.body.innerHTML = clean;
45401         }
45402         
45403     },
45404     
45405     cleanWordChars : function(input) {// change the chars to hex code
45406         var he = Roo.HtmlEditorCore;
45407         
45408         var output = input;
45409         Roo.each(he.swapCodes, function(sw) { 
45410             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
45411             
45412             output = output.replace(swapper, sw[1]);
45413         });
45414         
45415         return output;
45416     },
45417     
45418     
45419     cleanUpChildren : function (n)
45420     {
45421         if (!n.childNodes.length) {
45422             return;
45423         }
45424         for (var i = n.childNodes.length-1; i > -1 ; i--) {
45425            this.cleanUpChild(n.childNodes[i]);
45426         }
45427     },
45428     
45429     
45430         
45431     
45432     cleanUpChild : function (node)
45433     {
45434         var ed = this;
45435         //console.log(node);
45436         if (node.nodeName == "#text") {
45437             // clean up silly Windows -- stuff?
45438             return; 
45439         }
45440         if (node.nodeName == "#comment") {
45441             if (!this.allowComments) {
45442                 node.parentNode.removeChild(node);
45443             }
45444             // clean up silly Windows -- stuff?
45445             return; 
45446         }
45447         var lcname = node.tagName.toLowerCase();
45448         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
45449         // whitelist of tags..
45450         
45451         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
45452             // remove node.
45453             node.parentNode.removeChild(node);
45454             return;
45455             
45456         }
45457         
45458         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
45459         
45460         // spans with no attributes - just remove them..
45461         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
45462             remove_keep_children = true;
45463         }
45464         
45465         // remove <a name=....> as rendering on yahoo mailer is borked with this.
45466         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
45467         
45468         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
45469         //    remove_keep_children = true;
45470         //}
45471         
45472         if (remove_keep_children) {
45473             this.cleanUpChildren(node);
45474             // inserts everything just before this node...
45475             while (node.childNodes.length) {
45476                 var cn = node.childNodes[0];
45477                 node.removeChild(cn);
45478                 node.parentNode.insertBefore(cn, node);
45479             }
45480             node.parentNode.removeChild(node);
45481             return;
45482         }
45483         
45484         if (!node.attributes || !node.attributes.length) {
45485             
45486           
45487             
45488             
45489             this.cleanUpChildren(node);
45490             return;
45491         }
45492         
45493         function cleanAttr(n,v)
45494         {
45495             
45496             if (v.match(/^\./) || v.match(/^\//)) {
45497                 return;
45498             }
45499             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
45500                 return;
45501             }
45502             if (v.match(/^#/)) {
45503                 return;
45504             }
45505             if (v.match(/^\{/)) { // allow template editing.
45506                 return;
45507             }
45508 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45509             node.removeAttribute(n);
45510             
45511         }
45512         
45513         var cwhite = this.cwhite;
45514         var cblack = this.cblack;
45515             
45516         function cleanStyle(n,v)
45517         {
45518             if (v.match(/expression/)) { //XSS?? should we even bother..
45519                 node.removeAttribute(n);
45520                 return;
45521             }
45522             
45523             var parts = v.split(/;/);
45524             var clean = [];
45525             
45526             Roo.each(parts, function(p) {
45527                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45528                 if (!p.length) {
45529                     return true;
45530                 }
45531                 var l = p.split(':').shift().replace(/\s+/g,'');
45532                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45533                 
45534                 if ( cwhite.length && cblack.indexOf(l) > -1) {
45535 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
45536                     //node.removeAttribute(n);
45537                     return true;
45538                 }
45539                 //Roo.log()
45540                 // only allow 'c whitelisted system attributes'
45541                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
45542 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
45543                     //node.removeAttribute(n);
45544                     return true;
45545                 }
45546                 
45547                 
45548                  
45549                 
45550                 clean.push(p);
45551                 return true;
45552             });
45553             if (clean.length) { 
45554                 node.setAttribute(n, clean.join(';'));
45555             } else {
45556                 node.removeAttribute(n);
45557             }
45558             
45559         }
45560         
45561         
45562         for (var i = node.attributes.length-1; i > -1 ; i--) {
45563             var a = node.attributes[i];
45564             //console.log(a);
45565             
45566             if (a.name.toLowerCase().substr(0,2)=='on')  {
45567                 node.removeAttribute(a.name);
45568                 continue;
45569             }
45570             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
45571                 node.removeAttribute(a.name);
45572                 continue;
45573             }
45574             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
45575                 cleanAttr(a.name,a.value); // fixme..
45576                 continue;
45577             }
45578             if (a.name == 'style') {
45579                 cleanStyle(a.name,a.value);
45580                 continue;
45581             }
45582             /// clean up MS crap..
45583             // tecnically this should be a list of valid class'es..
45584             
45585             
45586             if (a.name == 'class') {
45587                 if (a.value.match(/^Mso/)) {
45588                     node.removeAttribute('class');
45589                 }
45590                 
45591                 if (a.value.match(/^body$/)) {
45592                     node.removeAttribute('class');
45593                 }
45594                 continue;
45595             }
45596             
45597             // style cleanup!?
45598             // class cleanup?
45599             
45600         }
45601         
45602         
45603         this.cleanUpChildren(node);
45604         
45605         
45606     },
45607     
45608     /**
45609      * Clean up MS wordisms...
45610      */
45611     cleanWord : function(node)
45612     {
45613         if (!node) {
45614             this.cleanWord(this.doc.body);
45615             return;
45616         }
45617         
45618         if(
45619                 node.nodeName == 'SPAN' &&
45620                 !node.hasAttributes() &&
45621                 node.childNodes.length == 1 &&
45622                 node.firstChild.nodeName == "#text"  
45623         ) {
45624             var textNode = node.firstChild;
45625             node.removeChild(textNode);
45626             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45627                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45628             }
45629             node.parentNode.insertBefore(textNode, node);
45630             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45631                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45632             }
45633             node.parentNode.removeChild(node);
45634         }
45635         
45636         if (node.nodeName == "#text") {
45637             // clean up silly Windows -- stuff?
45638             return; 
45639         }
45640         if (node.nodeName == "#comment") {
45641             node.parentNode.removeChild(node);
45642             // clean up silly Windows -- stuff?
45643             return; 
45644         }
45645         
45646         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45647             node.parentNode.removeChild(node);
45648             return;
45649         }
45650         //Roo.log(node.tagName);
45651         // remove - but keep children..
45652         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45653             //Roo.log('-- removed');
45654             while (node.childNodes.length) {
45655                 var cn = node.childNodes[0];
45656                 node.removeChild(cn);
45657                 node.parentNode.insertBefore(cn, node);
45658                 // move node to parent - and clean it..
45659                 this.cleanWord(cn);
45660             }
45661             node.parentNode.removeChild(node);
45662             /// no need to iterate chidlren = it's got none..
45663             //this.iterateChildren(node, this.cleanWord);
45664             return;
45665         }
45666         // clean styles
45667         if (node.className.length) {
45668             
45669             var cn = node.className.split(/\W+/);
45670             var cna = [];
45671             Roo.each(cn, function(cls) {
45672                 if (cls.match(/Mso[a-zA-Z]+/)) {
45673                     return;
45674                 }
45675                 cna.push(cls);
45676             });
45677             node.className = cna.length ? cna.join(' ') : '';
45678             if (!cna.length) {
45679                 node.removeAttribute("class");
45680             }
45681         }
45682         
45683         if (node.hasAttribute("lang")) {
45684             node.removeAttribute("lang");
45685         }
45686         
45687         if (node.hasAttribute("style")) {
45688             
45689             var styles = node.getAttribute("style").split(";");
45690             var nstyle = [];
45691             Roo.each(styles, function(s) {
45692                 if (!s.match(/:/)) {
45693                     return;
45694                 }
45695                 var kv = s.split(":");
45696                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45697                     return;
45698                 }
45699                 // what ever is left... we allow.
45700                 nstyle.push(s);
45701             });
45702             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45703             if (!nstyle.length) {
45704                 node.removeAttribute('style');
45705             }
45706         }
45707         this.iterateChildren(node, this.cleanWord);
45708         
45709         
45710         
45711     },
45712     /**
45713      * iterateChildren of a Node, calling fn each time, using this as the scole..
45714      * @param {DomNode} node node to iterate children of.
45715      * @param {Function} fn method of this class to call on each item.
45716      */
45717     iterateChildren : function(node, fn)
45718     {
45719         if (!node.childNodes.length) {
45720                 return;
45721         }
45722         for (var i = node.childNodes.length-1; i > -1 ; i--) {
45723            fn.call(this, node.childNodes[i])
45724         }
45725     },
45726     
45727     
45728     /**
45729      * cleanTableWidths.
45730      *
45731      * Quite often pasting from word etc.. results in tables with column and widths.
45732      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45733      *
45734      */
45735     cleanTableWidths : function(node)
45736     {
45737          
45738          
45739         if (!node) {
45740             this.cleanTableWidths(this.doc.body);
45741             return;
45742         }
45743         
45744         // ignore list...
45745         if (node.nodeName == "#text" || node.nodeName == "#comment") {
45746             return; 
45747         }
45748         Roo.log(node.tagName);
45749         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
45750             this.iterateChildren(node, this.cleanTableWidths);
45751             return;
45752         }
45753         if (node.hasAttribute('width')) {
45754             node.removeAttribute('width');
45755         }
45756         
45757          
45758         if (node.hasAttribute("style")) {
45759             // pretty basic...
45760             
45761             var styles = node.getAttribute("style").split(";");
45762             var nstyle = [];
45763             Roo.each(styles, function(s) {
45764                 if (!s.match(/:/)) {
45765                     return;
45766                 }
45767                 var kv = s.split(":");
45768                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45769                     return;
45770                 }
45771                 // what ever is left... we allow.
45772                 nstyle.push(s);
45773             });
45774             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45775             if (!nstyle.length) {
45776                 node.removeAttribute('style');
45777             }
45778         }
45779         
45780         this.iterateChildren(node, this.cleanTableWidths);
45781         
45782         
45783     },
45784     
45785     
45786     
45787     
45788     domToHTML : function(currentElement, depth, nopadtext) {
45789         
45790         depth = depth || 0;
45791         nopadtext = nopadtext || false;
45792     
45793         if (!currentElement) {
45794             return this.domToHTML(this.doc.body);
45795         }
45796         
45797         //Roo.log(currentElement);
45798         var j;
45799         var allText = false;
45800         var nodeName = currentElement.nodeName;
45801         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45802         
45803         if  (nodeName == '#text') {
45804             
45805             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45806         }
45807         
45808         
45809         var ret = '';
45810         if (nodeName != 'BODY') {
45811              
45812             var i = 0;
45813             // Prints the node tagName, such as <A>, <IMG>, etc
45814             if (tagName) {
45815                 var attr = [];
45816                 for(i = 0; i < currentElement.attributes.length;i++) {
45817                     // quoting?
45818                     var aname = currentElement.attributes.item(i).name;
45819                     if (!currentElement.attributes.item(i).value.length) {
45820                         continue;
45821                     }
45822                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45823                 }
45824                 
45825                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45826             } 
45827             else {
45828                 
45829                 // eack
45830             }
45831         } else {
45832             tagName = false;
45833         }
45834         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45835             return ret;
45836         }
45837         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45838             nopadtext = true;
45839         }
45840         
45841         
45842         // Traverse the tree
45843         i = 0;
45844         var currentElementChild = currentElement.childNodes.item(i);
45845         var allText = true;
45846         var innerHTML  = '';
45847         lastnode = '';
45848         while (currentElementChild) {
45849             // Formatting code (indent the tree so it looks nice on the screen)
45850             var nopad = nopadtext;
45851             if (lastnode == 'SPAN') {
45852                 nopad  = true;
45853             }
45854             // text
45855             if  (currentElementChild.nodeName == '#text') {
45856                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45857                 toadd = nopadtext ? toadd : toadd.trim();
45858                 if (!nopad && toadd.length > 80) {
45859                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45860                 }
45861                 innerHTML  += toadd;
45862                 
45863                 i++;
45864                 currentElementChild = currentElement.childNodes.item(i);
45865                 lastNode = '';
45866                 continue;
45867             }
45868             allText = false;
45869             
45870             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45871                 
45872             // Recursively traverse the tree structure of the child node
45873             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45874             lastnode = currentElementChild.nodeName;
45875             i++;
45876             currentElementChild=currentElement.childNodes.item(i);
45877         }
45878         
45879         ret += innerHTML;
45880         
45881         if (!allText) {
45882                 // The remaining code is mostly for formatting the tree
45883             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45884         }
45885         
45886         
45887         if (tagName) {
45888             ret+= "</"+tagName+">";
45889         }
45890         return ret;
45891         
45892     },
45893         
45894     applyBlacklists : function()
45895     {
45896         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45897         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45898         
45899         this.white = [];
45900         this.black = [];
45901         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45902             if (b.indexOf(tag) > -1) {
45903                 return;
45904             }
45905             this.white.push(tag);
45906             
45907         }, this);
45908         
45909         Roo.each(w, function(tag) {
45910             if (b.indexOf(tag) > -1) {
45911                 return;
45912             }
45913             if (this.white.indexOf(tag) > -1) {
45914                 return;
45915             }
45916             this.white.push(tag);
45917             
45918         }, this);
45919         
45920         
45921         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45922             if (w.indexOf(tag) > -1) {
45923                 return;
45924             }
45925             this.black.push(tag);
45926             
45927         }, this);
45928         
45929         Roo.each(b, function(tag) {
45930             if (w.indexOf(tag) > -1) {
45931                 return;
45932             }
45933             if (this.black.indexOf(tag) > -1) {
45934                 return;
45935             }
45936             this.black.push(tag);
45937             
45938         }, this);
45939         
45940         
45941         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45942         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45943         
45944         this.cwhite = [];
45945         this.cblack = [];
45946         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45947             if (b.indexOf(tag) > -1) {
45948                 return;
45949             }
45950             this.cwhite.push(tag);
45951             
45952         }, this);
45953         
45954         Roo.each(w, function(tag) {
45955             if (b.indexOf(tag) > -1) {
45956                 return;
45957             }
45958             if (this.cwhite.indexOf(tag) > -1) {
45959                 return;
45960             }
45961             this.cwhite.push(tag);
45962             
45963         }, this);
45964         
45965         
45966         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45967             if (w.indexOf(tag) > -1) {
45968                 return;
45969             }
45970             this.cblack.push(tag);
45971             
45972         }, this);
45973         
45974         Roo.each(b, function(tag) {
45975             if (w.indexOf(tag) > -1) {
45976                 return;
45977             }
45978             if (this.cblack.indexOf(tag) > -1) {
45979                 return;
45980             }
45981             this.cblack.push(tag);
45982             
45983         }, this);
45984     },
45985     
45986     setStylesheets : function(stylesheets)
45987     {
45988         if(typeof(stylesheets) == 'string'){
45989             Roo.get(this.iframe.contentDocument.head).createChild({
45990                 tag : 'link',
45991                 rel : 'stylesheet',
45992                 type : 'text/css',
45993                 href : stylesheets
45994             });
45995             
45996             return;
45997         }
45998         var _this = this;
45999      
46000         Roo.each(stylesheets, function(s) {
46001             if(!s.length){
46002                 return;
46003             }
46004             
46005             Roo.get(_this.iframe.contentDocument.head).createChild({
46006                 tag : 'link',
46007                 rel : 'stylesheet',
46008                 type : 'text/css',
46009                 href : s
46010             });
46011         });
46012
46013         
46014     },
46015     
46016     removeStylesheets : function()
46017     {
46018         var _this = this;
46019         
46020         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46021             s.remove();
46022         });
46023     },
46024     
46025     setStyle : function(style)
46026     {
46027         Roo.get(this.iframe.contentDocument.head).createChild({
46028             tag : 'style',
46029             type : 'text/css',
46030             html : style
46031         });
46032
46033         return;
46034     }
46035     
46036     // hide stuff that is not compatible
46037     /**
46038      * @event blur
46039      * @hide
46040      */
46041     /**
46042      * @event change
46043      * @hide
46044      */
46045     /**
46046      * @event focus
46047      * @hide
46048      */
46049     /**
46050      * @event specialkey
46051      * @hide
46052      */
46053     /**
46054      * @cfg {String} fieldClass @hide
46055      */
46056     /**
46057      * @cfg {String} focusClass @hide
46058      */
46059     /**
46060      * @cfg {String} autoCreate @hide
46061      */
46062     /**
46063      * @cfg {String} inputType @hide
46064      */
46065     /**
46066      * @cfg {String} invalidClass @hide
46067      */
46068     /**
46069      * @cfg {String} invalidText @hide
46070      */
46071     /**
46072      * @cfg {String} msgFx @hide
46073      */
46074     /**
46075      * @cfg {String} validateOnBlur @hide
46076      */
46077 });
46078
46079 Roo.HtmlEditorCore.white = [
46080         'area', 'br', 'img', 'input', 'hr', 'wbr',
46081         
46082        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46083        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46084        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46085        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46086        'table',   'ul',         'xmp', 
46087        
46088        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46089       'thead',   'tr', 
46090      
46091       'dir', 'menu', 'ol', 'ul', 'dl',
46092        
46093       'embed',  'object'
46094 ];
46095
46096
46097 Roo.HtmlEditorCore.black = [
46098     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46099         'applet', // 
46100         'base',   'basefont', 'bgsound', 'blink',  'body', 
46101         'frame',  'frameset', 'head',    'html',   'ilayer', 
46102         'iframe', 'layer',  'link',     'meta',    'object',   
46103         'script', 'style' ,'title',  'xml' // clean later..
46104 ];
46105 Roo.HtmlEditorCore.clean = [
46106     'script', 'style', 'title', 'xml'
46107 ];
46108 Roo.HtmlEditorCore.remove = [
46109     'font'
46110 ];
46111 // attributes..
46112
46113 Roo.HtmlEditorCore.ablack = [
46114     'on'
46115 ];
46116     
46117 Roo.HtmlEditorCore.aclean = [ 
46118     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46119 ];
46120
46121 // protocols..
46122 Roo.HtmlEditorCore.pwhite= [
46123         'http',  'https',  'mailto'
46124 ];
46125
46126 // white listed style attributes.
46127 Roo.HtmlEditorCore.cwhite= [
46128       //  'text-align', /// default is to allow most things..
46129       
46130          
46131 //        'font-size'//??
46132 ];
46133
46134 // black listed style attributes.
46135 Roo.HtmlEditorCore.cblack= [
46136       //  'font-size' -- this can be set by the project 
46137 ];
46138
46139
46140 Roo.HtmlEditorCore.swapCodes   =[ 
46141     [    8211, "&#8211;" ], 
46142     [    8212, "&#8212;" ], 
46143     [    8216,  "'" ],  
46144     [    8217, "'" ],  
46145     [    8220, '"' ],  
46146     [    8221, '"' ],  
46147     [    8226, "*" ],  
46148     [    8230, "..." ]
46149 ]; 
46150
46151     //<script type="text/javascript">
46152
46153 /*
46154  * Ext JS Library 1.1.1
46155  * Copyright(c) 2006-2007, Ext JS, LLC.
46156  * Licence LGPL
46157  * 
46158  */
46159  
46160  
46161 Roo.form.HtmlEditor = function(config){
46162     
46163     
46164     
46165     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46166     
46167     if (!this.toolbars) {
46168         this.toolbars = [];
46169     }
46170     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46171     
46172     
46173 };
46174
46175 /**
46176  * @class Roo.form.HtmlEditor
46177  * @extends Roo.form.Field
46178  * Provides a lightweight HTML Editor component.
46179  *
46180  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46181  * 
46182  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46183  * supported by this editor.</b><br/><br/>
46184  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46185  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46186  */
46187 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46188     /**
46189      * @cfg {Boolean} clearUp
46190      */
46191     clearUp : true,
46192       /**
46193      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46194      */
46195     toolbars : false,
46196    
46197      /**
46198      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46199      *                        Roo.resizable.
46200      */
46201     resizable : false,
46202      /**
46203      * @cfg {Number} height (in pixels)
46204      */   
46205     height: 300,
46206    /**
46207      * @cfg {Number} width (in pixels)
46208      */   
46209     width: 500,
46210     
46211     /**
46212      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46213      * 
46214      */
46215     stylesheets: false,
46216     
46217     
46218      /**
46219      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46220      * 
46221      */
46222     cblack: false,
46223     /**
46224      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46225      * 
46226      */
46227     cwhite: false,
46228     
46229      /**
46230      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46231      * 
46232      */
46233     black: false,
46234     /**
46235      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46236      * 
46237      */
46238     white: false,
46239     /**
46240      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46241      */
46242     allowComments: false,
46243     
46244     // id of frame..
46245     frameId: false,
46246     
46247     // private properties
46248     validationEvent : false,
46249     deferHeight: true,
46250     initialized : false,
46251     activated : false,
46252     
46253     onFocus : Roo.emptyFn,
46254     iframePad:3,
46255     hideMode:'offsets',
46256     
46257     actionMode : 'container', // defaults to hiding it...
46258     
46259     defaultAutoCreate : { // modified by initCompnoent..
46260         tag: "textarea",
46261         style:"width:500px;height:300px;",
46262         autocomplete: "new-password"
46263     },
46264
46265     // private
46266     initComponent : function(){
46267         this.addEvents({
46268             /**
46269              * @event initialize
46270              * Fires when the editor is fully initialized (including the iframe)
46271              * @param {HtmlEditor} this
46272              */
46273             initialize: true,
46274             /**
46275              * @event activate
46276              * Fires when the editor is first receives the focus. Any insertion must wait
46277              * until after this event.
46278              * @param {HtmlEditor} this
46279              */
46280             activate: true,
46281              /**
46282              * @event beforesync
46283              * Fires before the textarea is updated with content from the editor iframe. Return false
46284              * to cancel the sync.
46285              * @param {HtmlEditor} this
46286              * @param {String} html
46287              */
46288             beforesync: true,
46289              /**
46290              * @event beforepush
46291              * Fires before the iframe editor is updated with content from the textarea. Return false
46292              * to cancel the push.
46293              * @param {HtmlEditor} this
46294              * @param {String} html
46295              */
46296             beforepush: true,
46297              /**
46298              * @event sync
46299              * Fires when the textarea is updated with content from the editor iframe.
46300              * @param {HtmlEditor} this
46301              * @param {String} html
46302              */
46303             sync: true,
46304              /**
46305              * @event push
46306              * Fires when the iframe editor is updated with content from the textarea.
46307              * @param {HtmlEditor} this
46308              * @param {String} html
46309              */
46310             push: true,
46311              /**
46312              * @event editmodechange
46313              * Fires when the editor switches edit modes
46314              * @param {HtmlEditor} this
46315              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46316              */
46317             editmodechange: true,
46318             /**
46319              * @event editorevent
46320              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46321              * @param {HtmlEditor} this
46322              */
46323             editorevent: true,
46324             /**
46325              * @event firstfocus
46326              * Fires when on first focus - needed by toolbars..
46327              * @param {HtmlEditor} this
46328              */
46329             firstfocus: true,
46330             /**
46331              * @event autosave
46332              * Auto save the htmlEditor value as a file into Events
46333              * @param {HtmlEditor} this
46334              */
46335             autosave: true,
46336             /**
46337              * @event savedpreview
46338              * preview the saved version of htmlEditor
46339              * @param {HtmlEditor} this
46340              */
46341             savedpreview: true,
46342             
46343             /**
46344             * @event stylesheetsclick
46345             * Fires when press the Sytlesheets button
46346             * @param {Roo.HtmlEditorCore} this
46347             */
46348             stylesheetsclick: true
46349         });
46350         this.defaultAutoCreate =  {
46351             tag: "textarea",
46352             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46353             autocomplete: "new-password"
46354         };
46355     },
46356
46357     /**
46358      * Protected method that will not generally be called directly. It
46359      * is called when the editor creates its toolbar. Override this method if you need to
46360      * add custom toolbar buttons.
46361      * @param {HtmlEditor} editor
46362      */
46363     createToolbar : function(editor){
46364         Roo.log("create toolbars");
46365         if (!editor.toolbars || !editor.toolbars.length) {
46366             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46367         }
46368         
46369         for (var i =0 ; i < editor.toolbars.length;i++) {
46370             editor.toolbars[i] = Roo.factory(
46371                     typeof(editor.toolbars[i]) == 'string' ?
46372                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46373                 Roo.form.HtmlEditor);
46374             editor.toolbars[i].init(editor);
46375         }
46376          
46377         
46378     },
46379
46380      
46381     // private
46382     onRender : function(ct, position)
46383     {
46384         var _t = this;
46385         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46386         
46387         this.wrap = this.el.wrap({
46388             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46389         });
46390         
46391         this.editorcore.onRender(ct, position);
46392          
46393         if (this.resizable) {
46394             this.resizeEl = new Roo.Resizable(this.wrap, {
46395                 pinned : true,
46396                 wrap: true,
46397                 dynamic : true,
46398                 minHeight : this.height,
46399                 height: this.height,
46400                 handles : this.resizable,
46401                 width: this.width,
46402                 listeners : {
46403                     resize : function(r, w, h) {
46404                         _t.onResize(w,h); // -something
46405                     }
46406                 }
46407             });
46408             
46409         }
46410         this.createToolbar(this);
46411        
46412         
46413         if(!this.width){
46414             this.setSize(this.wrap.getSize());
46415         }
46416         if (this.resizeEl) {
46417             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46418             // should trigger onReize..
46419         }
46420         
46421         this.keyNav = new Roo.KeyNav(this.el, {
46422             
46423             "tab" : function(e){
46424                 e.preventDefault();
46425                 
46426                 var value = this.getValue();
46427                 
46428                 var start = this.el.dom.selectionStart;
46429                 var end = this.el.dom.selectionEnd;
46430                 
46431                 if(!e.shiftKey){
46432                     
46433                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46434                     this.el.dom.setSelectionRange(end + 1, end + 1);
46435                     return;
46436                 }
46437                 
46438                 var f = value.substring(0, start).split("\t");
46439                 
46440                 if(f.pop().length != 0){
46441                     return;
46442                 }
46443                 
46444                 this.setValue(f.join("\t") + value.substring(end));
46445                 this.el.dom.setSelectionRange(start - 1, start - 1);
46446                 
46447             },
46448             
46449             "home" : function(e){
46450                 e.preventDefault();
46451                 
46452                 var curr = this.el.dom.selectionStart;
46453                 var lines = this.getValue().split("\n");
46454                 
46455                 if(!lines.length){
46456                     return;
46457                 }
46458                 
46459                 if(e.ctrlKey){
46460                     this.el.dom.setSelectionRange(0, 0);
46461                     return;
46462                 }
46463                 
46464                 var pos = 0;
46465                 
46466                 for (var i = 0; i < lines.length;i++) {
46467                     pos += lines[i].length;
46468                     
46469                     if(i != 0){
46470                         pos += 1;
46471                     }
46472                     
46473                     if(pos < curr){
46474                         continue;
46475                     }
46476                     
46477                     pos -= lines[i].length;
46478                     
46479                     break;
46480                 }
46481                 
46482                 if(!e.shiftKey){
46483                     this.el.dom.setSelectionRange(pos, pos);
46484                     return;
46485                 }
46486                 
46487                 this.el.dom.selectionStart = pos;
46488                 this.el.dom.selectionEnd = curr;
46489             },
46490             
46491             "end" : function(e){
46492                 e.preventDefault();
46493                 
46494                 var curr = this.el.dom.selectionStart;
46495                 var lines = this.getValue().split("\n");
46496                 
46497                 if(!lines.length){
46498                     return;
46499                 }
46500                 
46501                 if(e.ctrlKey){
46502                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
46503                     return;
46504                 }
46505                 
46506                 var pos = 0;
46507                 
46508                 for (var i = 0; i < lines.length;i++) {
46509                     
46510                     pos += lines[i].length;
46511                     
46512                     if(i != 0){
46513                         pos += 1;
46514                     }
46515                     
46516                     if(pos < curr){
46517                         continue;
46518                     }
46519                     
46520                     break;
46521                 }
46522                 
46523                 if(!e.shiftKey){
46524                     this.el.dom.setSelectionRange(pos, pos);
46525                     return;
46526                 }
46527                 
46528                 this.el.dom.selectionStart = curr;
46529                 this.el.dom.selectionEnd = pos;
46530             },
46531
46532             scope : this,
46533
46534             doRelay : function(foo, bar, hname){
46535                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46536             },
46537
46538             forceKeyDown: true
46539         });
46540         
46541 //        if(this.autosave && this.w){
46542 //            this.autoSaveFn = setInterval(this.autosave, 1000);
46543 //        }
46544     },
46545
46546     // private
46547     onResize : function(w, h)
46548     {
46549         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
46550         var ew = false;
46551         var eh = false;
46552         
46553         if(this.el ){
46554             if(typeof w == 'number'){
46555                 var aw = w - this.wrap.getFrameWidth('lr');
46556                 this.el.setWidth(this.adjustWidth('textarea', aw));
46557                 ew = aw;
46558             }
46559             if(typeof h == 'number'){
46560                 var tbh = 0;
46561                 for (var i =0; i < this.toolbars.length;i++) {
46562                     // fixme - ask toolbars for heights?
46563                     tbh += this.toolbars[i].tb.el.getHeight();
46564                     if (this.toolbars[i].footer) {
46565                         tbh += this.toolbars[i].footer.el.getHeight();
46566                     }
46567                 }
46568                 
46569                 
46570                 
46571                 
46572                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
46573                 ah -= 5; // knock a few pixes off for look..
46574 //                Roo.log(ah);
46575                 this.el.setHeight(this.adjustWidth('textarea', ah));
46576                 var eh = ah;
46577             }
46578         }
46579         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
46580         this.editorcore.onResize(ew,eh);
46581         
46582     },
46583
46584     /**
46585      * Toggles the editor between standard and source edit mode.
46586      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46587      */
46588     toggleSourceEdit : function(sourceEditMode)
46589     {
46590         this.editorcore.toggleSourceEdit(sourceEditMode);
46591         
46592         if(this.editorcore.sourceEditMode){
46593             Roo.log('editor - showing textarea');
46594             
46595 //            Roo.log('in');
46596 //            Roo.log(this.syncValue());
46597             this.editorcore.syncValue();
46598             this.el.removeClass('x-hidden');
46599             this.el.dom.removeAttribute('tabIndex');
46600             this.el.focus();
46601             
46602             for (var i = 0; i < this.toolbars.length; i++) {
46603                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46604                     this.toolbars[i].tb.hide();
46605                     this.toolbars[i].footer.hide();
46606                 }
46607             }
46608             
46609         }else{
46610             Roo.log('editor - hiding textarea');
46611 //            Roo.log('out')
46612 //            Roo.log(this.pushValue()); 
46613             this.editorcore.pushValue();
46614             
46615             this.el.addClass('x-hidden');
46616             this.el.dom.setAttribute('tabIndex', -1);
46617             
46618             for (var i = 0; i < this.toolbars.length; i++) {
46619                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46620                     this.toolbars[i].tb.show();
46621                     this.toolbars[i].footer.show();
46622                 }
46623             }
46624             
46625             //this.deferFocus();
46626         }
46627         
46628         this.setSize(this.wrap.getSize());
46629         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
46630         
46631         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
46632     },
46633  
46634     // private (for BoxComponent)
46635     adjustSize : Roo.BoxComponent.prototype.adjustSize,
46636
46637     // private (for BoxComponent)
46638     getResizeEl : function(){
46639         return this.wrap;
46640     },
46641
46642     // private (for BoxComponent)
46643     getPositionEl : function(){
46644         return this.wrap;
46645     },
46646
46647     // private
46648     initEvents : function(){
46649         this.originalValue = this.getValue();
46650     },
46651
46652     /**
46653      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46654      * @method
46655      */
46656     markInvalid : Roo.emptyFn,
46657     /**
46658      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46659      * @method
46660      */
46661     clearInvalid : Roo.emptyFn,
46662
46663     setValue : function(v){
46664         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
46665         this.editorcore.pushValue();
46666     },
46667
46668      
46669     // private
46670     deferFocus : function(){
46671         this.focus.defer(10, this);
46672     },
46673
46674     // doc'ed in Field
46675     focus : function(){
46676         this.editorcore.focus();
46677         
46678     },
46679       
46680
46681     // private
46682     onDestroy : function(){
46683         
46684         
46685         
46686         if(this.rendered){
46687             
46688             for (var i =0; i < this.toolbars.length;i++) {
46689                 // fixme - ask toolbars for heights?
46690                 this.toolbars[i].onDestroy();
46691             }
46692             
46693             this.wrap.dom.innerHTML = '';
46694             this.wrap.remove();
46695         }
46696     },
46697
46698     // private
46699     onFirstFocus : function(){
46700         //Roo.log("onFirstFocus");
46701         this.editorcore.onFirstFocus();
46702          for (var i =0; i < this.toolbars.length;i++) {
46703             this.toolbars[i].onFirstFocus();
46704         }
46705         
46706     },
46707     
46708     // private
46709     syncValue : function()
46710     {
46711         this.editorcore.syncValue();
46712     },
46713     
46714     pushValue : function()
46715     {
46716         this.editorcore.pushValue();
46717     },
46718     
46719     setStylesheets : function(stylesheets)
46720     {
46721         this.editorcore.setStylesheets(stylesheets);
46722     },
46723     
46724     removeStylesheets : function()
46725     {
46726         this.editorcore.removeStylesheets();
46727     }
46728      
46729     
46730     // hide stuff that is not compatible
46731     /**
46732      * @event blur
46733      * @hide
46734      */
46735     /**
46736      * @event change
46737      * @hide
46738      */
46739     /**
46740      * @event focus
46741      * @hide
46742      */
46743     /**
46744      * @event specialkey
46745      * @hide
46746      */
46747     /**
46748      * @cfg {String} fieldClass @hide
46749      */
46750     /**
46751      * @cfg {String} focusClass @hide
46752      */
46753     /**
46754      * @cfg {String} autoCreate @hide
46755      */
46756     /**
46757      * @cfg {String} inputType @hide
46758      */
46759     /**
46760      * @cfg {String} invalidClass @hide
46761      */
46762     /**
46763      * @cfg {String} invalidText @hide
46764      */
46765     /**
46766      * @cfg {String} msgFx @hide
46767      */
46768     /**
46769      * @cfg {String} validateOnBlur @hide
46770      */
46771 });
46772  
46773     // <script type="text/javascript">
46774 /*
46775  * Based on
46776  * Ext JS Library 1.1.1
46777  * Copyright(c) 2006-2007, Ext JS, LLC.
46778  *  
46779  
46780  */
46781
46782 /**
46783  * @class Roo.form.HtmlEditorToolbar1
46784  * Basic Toolbar
46785  * 
46786  * Usage:
46787  *
46788  new Roo.form.HtmlEditor({
46789     ....
46790     toolbars : [
46791         new Roo.form.HtmlEditorToolbar1({
46792             disable : { fonts: 1 , format: 1, ..., ... , ...],
46793             btns : [ .... ]
46794         })
46795     }
46796      
46797  * 
46798  * @cfg {Object} disable List of elements to disable..
46799  * @cfg {Array} btns List of additional buttons.
46800  * 
46801  * 
46802  * NEEDS Extra CSS? 
46803  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46804  */
46805  
46806 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46807 {
46808     
46809     Roo.apply(this, config);
46810     
46811     // default disabled, based on 'good practice'..
46812     this.disable = this.disable || {};
46813     Roo.applyIf(this.disable, {
46814         fontSize : true,
46815         colors : true,
46816         specialElements : true
46817     });
46818     
46819     
46820     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46821     // dont call parent... till later.
46822 }
46823
46824 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46825     
46826     tb: false,
46827     
46828     rendered: false,
46829     
46830     editor : false,
46831     editorcore : false,
46832     /**
46833      * @cfg {Object} disable  List of toolbar elements to disable
46834          
46835      */
46836     disable : false,
46837     
46838     
46839      /**
46840      * @cfg {String} createLinkText The default text for the create link prompt
46841      */
46842     createLinkText : 'Please enter the URL for the link:',
46843     /**
46844      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46845      */
46846     defaultLinkValue : 'http:/'+'/',
46847    
46848     
46849       /**
46850      * @cfg {Array} fontFamilies An array of available font families
46851      */
46852     fontFamilies : [
46853         'Arial',
46854         'Courier New',
46855         'Tahoma',
46856         'Times New Roman',
46857         'Verdana'
46858     ],
46859     
46860     specialChars : [
46861            "&#169;",
46862           "&#174;",     
46863           "&#8482;",    
46864           "&#163;" ,    
46865          // "&#8212;",    
46866           "&#8230;",    
46867           "&#247;" ,    
46868         //  "&#225;" ,     ?? a acute?
46869            "&#8364;"    , //Euro
46870        //   "&#8220;"    ,
46871         //  "&#8221;"    ,
46872         //  "&#8226;"    ,
46873           "&#176;"  //   , // degrees
46874
46875          // "&#233;"     , // e ecute
46876          // "&#250;"     , // u ecute?
46877     ],
46878     
46879     specialElements : [
46880         {
46881             text: "Insert Table",
46882             xtype: 'MenuItem',
46883             xns : Roo.Menu,
46884             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46885                 
46886         },
46887         {    
46888             text: "Insert Image",
46889             xtype: 'MenuItem',
46890             xns : Roo.Menu,
46891             ihtml : '<img src="about:blank"/>'
46892             
46893         }
46894         
46895          
46896     ],
46897     
46898     
46899     inputElements : [ 
46900             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46901             "input:submit", "input:button", "select", "textarea", "label" ],
46902     formats : [
46903         ["p"] ,  
46904         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46905         ["pre"],[ "code"], 
46906         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46907         ['div'],['span'],
46908         ['sup'],['sub']
46909     ],
46910     
46911     cleanStyles : [
46912         "font-size"
46913     ],
46914      /**
46915      * @cfg {String} defaultFont default font to use.
46916      */
46917     defaultFont: 'tahoma',
46918    
46919     fontSelect : false,
46920     
46921     
46922     formatCombo : false,
46923     
46924     init : function(editor)
46925     {
46926         this.editor = editor;
46927         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46928         var editorcore = this.editorcore;
46929         
46930         var _t = this;
46931         
46932         var fid = editorcore.frameId;
46933         var etb = this;
46934         function btn(id, toggle, handler){
46935             var xid = fid + '-'+ id ;
46936             return {
46937                 id : xid,
46938                 cmd : id,
46939                 cls : 'x-btn-icon x-edit-'+id,
46940                 enableToggle:toggle !== false,
46941                 scope: _t, // was editor...
46942                 handler:handler||_t.relayBtnCmd,
46943                 clickEvent:'mousedown',
46944                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46945                 tabIndex:-1
46946             };
46947         }
46948         
46949         
46950         
46951         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46952         this.tb = tb;
46953          // stop form submits
46954         tb.el.on('click', function(e){
46955             e.preventDefault(); // what does this do?
46956         });
46957
46958         if(!this.disable.font) { // && !Roo.isSafari){
46959             /* why no safari for fonts 
46960             editor.fontSelect = tb.el.createChild({
46961                 tag:'select',
46962                 tabIndex: -1,
46963                 cls:'x-font-select',
46964                 html: this.createFontOptions()
46965             });
46966             
46967             editor.fontSelect.on('change', function(){
46968                 var font = editor.fontSelect.dom.value;
46969                 editor.relayCmd('fontname', font);
46970                 editor.deferFocus();
46971             }, editor);
46972             
46973             tb.add(
46974                 editor.fontSelect.dom,
46975                 '-'
46976             );
46977             */
46978             
46979         };
46980         if(!this.disable.formats){
46981             this.formatCombo = new Roo.form.ComboBox({
46982                 store: new Roo.data.SimpleStore({
46983                     id : 'tag',
46984                     fields: ['tag'],
46985                     data : this.formats // from states.js
46986                 }),
46987                 blockFocus : true,
46988                 name : '',
46989                 //autoCreate : {tag: "div",  size: "20"},
46990                 displayField:'tag',
46991                 typeAhead: false,
46992                 mode: 'local',
46993                 editable : false,
46994                 triggerAction: 'all',
46995                 emptyText:'Add tag',
46996                 selectOnFocus:true,
46997                 width:135,
46998                 listeners : {
46999                     'select': function(c, r, i) {
47000                         editorcore.insertTag(r.get('tag'));
47001                         editor.focus();
47002                     }
47003                 }
47004
47005             });
47006             tb.addField(this.formatCombo);
47007             
47008         }
47009         
47010         if(!this.disable.format){
47011             tb.add(
47012                 btn('bold'),
47013                 btn('italic'),
47014                 btn('underline'),
47015                 btn('strikethrough')
47016             );
47017         };
47018         if(!this.disable.fontSize){
47019             tb.add(
47020                 '-',
47021                 
47022                 
47023                 btn('increasefontsize', false, editorcore.adjustFont),
47024                 btn('decreasefontsize', false, editorcore.adjustFont)
47025             );
47026         };
47027         
47028         
47029         if(!this.disable.colors){
47030             tb.add(
47031                 '-', {
47032                     id:editorcore.frameId +'-forecolor',
47033                     cls:'x-btn-icon x-edit-forecolor',
47034                     clickEvent:'mousedown',
47035                     tooltip: this.buttonTips['forecolor'] || undefined,
47036                     tabIndex:-1,
47037                     menu : new Roo.menu.ColorMenu({
47038                         allowReselect: true,
47039                         focus: Roo.emptyFn,
47040                         value:'000000',
47041                         plain:true,
47042                         selectHandler: function(cp, color){
47043                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47044                             editor.deferFocus();
47045                         },
47046                         scope: editorcore,
47047                         clickEvent:'mousedown'
47048                     })
47049                 }, {
47050                     id:editorcore.frameId +'backcolor',
47051                     cls:'x-btn-icon x-edit-backcolor',
47052                     clickEvent:'mousedown',
47053                     tooltip: this.buttonTips['backcolor'] || undefined,
47054                     tabIndex:-1,
47055                     menu : new Roo.menu.ColorMenu({
47056                         focus: Roo.emptyFn,
47057                         value:'FFFFFF',
47058                         plain:true,
47059                         allowReselect: true,
47060                         selectHandler: function(cp, color){
47061                             if(Roo.isGecko){
47062                                 editorcore.execCmd('useCSS', false);
47063                                 editorcore.execCmd('hilitecolor', color);
47064                                 editorcore.execCmd('useCSS', true);
47065                                 editor.deferFocus();
47066                             }else{
47067                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47068                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47069                                 editor.deferFocus();
47070                             }
47071                         },
47072                         scope:editorcore,
47073                         clickEvent:'mousedown'
47074                     })
47075                 }
47076             );
47077         };
47078         // now add all the items...
47079         
47080
47081         if(!this.disable.alignments){
47082             tb.add(
47083                 '-',
47084                 btn('justifyleft'),
47085                 btn('justifycenter'),
47086                 btn('justifyright')
47087             );
47088         };
47089
47090         //if(!Roo.isSafari){
47091             if(!this.disable.links){
47092                 tb.add(
47093                     '-',
47094                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47095                 );
47096             };
47097
47098             if(!this.disable.lists){
47099                 tb.add(
47100                     '-',
47101                     btn('insertorderedlist'),
47102                     btn('insertunorderedlist')
47103                 );
47104             }
47105             if(!this.disable.sourceEdit){
47106                 tb.add(
47107                     '-',
47108                     btn('sourceedit', true, function(btn){
47109                         this.toggleSourceEdit(btn.pressed);
47110                     })
47111                 );
47112             }
47113         //}
47114         
47115         var smenu = { };
47116         // special menu.. - needs to be tidied up..
47117         if (!this.disable.special) {
47118             smenu = {
47119                 text: "&#169;",
47120                 cls: 'x-edit-none',
47121                 
47122                 menu : {
47123                     items : []
47124                 }
47125             };
47126             for (var i =0; i < this.specialChars.length; i++) {
47127                 smenu.menu.items.push({
47128                     
47129                     html: this.specialChars[i],
47130                     handler: function(a,b) {
47131                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47132                         //editor.insertAtCursor(a.html);
47133                         
47134                     },
47135                     tabIndex:-1
47136                 });
47137             }
47138             
47139             
47140             tb.add(smenu);
47141             
47142             
47143         }
47144         
47145         var cmenu = { };
47146         if (!this.disable.cleanStyles) {
47147             cmenu = {
47148                 cls: 'x-btn-icon x-btn-clear',
47149                 
47150                 menu : {
47151                     items : []
47152                 }
47153             };
47154             for (var i =0; i < this.cleanStyles.length; i++) {
47155                 cmenu.menu.items.push({
47156                     actiontype : this.cleanStyles[i],
47157                     html: 'Remove ' + this.cleanStyles[i],
47158                     handler: function(a,b) {
47159 //                        Roo.log(a);
47160 //                        Roo.log(b);
47161                         var c = Roo.get(editorcore.doc.body);
47162                         c.select('[style]').each(function(s) {
47163                             s.dom.style.removeProperty(a.actiontype);
47164                         });
47165                         editorcore.syncValue();
47166                     },
47167                     tabIndex:-1
47168                 });
47169             }
47170              cmenu.menu.items.push({
47171                 actiontype : 'tablewidths',
47172                 html: 'Remove Table Widths',
47173                 handler: function(a,b) {
47174                     editorcore.cleanTableWidths();
47175                     editorcore.syncValue();
47176                 },
47177                 tabIndex:-1
47178             });
47179             cmenu.menu.items.push({
47180                 actiontype : 'word',
47181                 html: 'Remove MS Word Formating',
47182                 handler: function(a,b) {
47183                     editorcore.cleanWord();
47184                     editorcore.syncValue();
47185                 },
47186                 tabIndex:-1
47187             });
47188             
47189             cmenu.menu.items.push({
47190                 actiontype : 'all',
47191                 html: 'Remove All Styles',
47192                 handler: function(a,b) {
47193                     
47194                     var c = Roo.get(editorcore.doc.body);
47195                     c.select('[style]').each(function(s) {
47196                         s.dom.removeAttribute('style');
47197                     });
47198                     editorcore.syncValue();
47199                 },
47200                 tabIndex:-1
47201             });
47202             
47203             cmenu.menu.items.push({
47204                 actiontype : 'all',
47205                 html: 'Remove All CSS Classes',
47206                 handler: function(a,b) {
47207                     
47208                     var c = Roo.get(editorcore.doc.body);
47209                     c.select('[class]').each(function(s) {
47210                         s.dom.removeAttribute('class');
47211                     });
47212                     editorcore.cleanWord();
47213                     editorcore.syncValue();
47214                 },
47215                 tabIndex:-1
47216             });
47217             
47218              cmenu.menu.items.push({
47219                 actiontype : 'tidy',
47220                 html: 'Tidy HTML Source',
47221                 handler: function(a,b) {
47222                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
47223                     editorcore.syncValue();
47224                 },
47225                 tabIndex:-1
47226             });
47227             
47228             
47229             tb.add(cmenu);
47230         }
47231          
47232         if (!this.disable.specialElements) {
47233             var semenu = {
47234                 text: "Other;",
47235                 cls: 'x-edit-none',
47236                 menu : {
47237                     items : []
47238                 }
47239             };
47240             for (var i =0; i < this.specialElements.length; i++) {
47241                 semenu.menu.items.push(
47242                     Roo.apply({ 
47243                         handler: function(a,b) {
47244                             editor.insertAtCursor(this.ihtml);
47245                         }
47246                     }, this.specialElements[i])
47247                 );
47248                     
47249             }
47250             
47251             tb.add(semenu);
47252             
47253             
47254         }
47255          
47256         
47257         if (this.btns) {
47258             for(var i =0; i< this.btns.length;i++) {
47259                 var b = Roo.factory(this.btns[i],Roo.form);
47260                 b.cls =  'x-edit-none';
47261                 
47262                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47263                     b.cls += ' x-init-enable';
47264                 }
47265                 
47266                 b.scope = editorcore;
47267                 tb.add(b);
47268             }
47269         
47270         }
47271         
47272         
47273         
47274         // disable everything...
47275         
47276         this.tb.items.each(function(item){
47277             
47278            if(
47279                 item.id != editorcore.frameId+ '-sourceedit' && 
47280                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47281             ){
47282                 
47283                 item.disable();
47284             }
47285         });
47286         this.rendered = true;
47287         
47288         // the all the btns;
47289         editor.on('editorevent', this.updateToolbar, this);
47290         // other toolbars need to implement this..
47291         //editor.on('editmodechange', this.updateToolbar, this);
47292     },
47293     
47294     
47295     relayBtnCmd : function(btn) {
47296         this.editorcore.relayCmd(btn.cmd);
47297     },
47298     // private used internally
47299     createLink : function(){
47300         Roo.log("create link?");
47301         var url = prompt(this.createLinkText, this.defaultLinkValue);
47302         if(url && url != 'http:/'+'/'){
47303             this.editorcore.relayCmd('createlink', url);
47304         }
47305     },
47306
47307     
47308     /**
47309      * Protected method that will not generally be called directly. It triggers
47310      * a toolbar update by reading the markup state of the current selection in the editor.
47311      */
47312     updateToolbar: function(){
47313
47314         if(!this.editorcore.activated){
47315             this.editor.onFirstFocus();
47316             return;
47317         }
47318
47319         var btns = this.tb.items.map, 
47320             doc = this.editorcore.doc,
47321             frameId = this.editorcore.frameId;
47322
47323         if(!this.disable.font && !Roo.isSafari){
47324             /*
47325             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47326             if(name != this.fontSelect.dom.value){
47327                 this.fontSelect.dom.value = name;
47328             }
47329             */
47330         }
47331         if(!this.disable.format){
47332             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47333             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47334             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47335             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47336         }
47337         if(!this.disable.alignments){
47338             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47339             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47340             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47341         }
47342         if(!Roo.isSafari && !this.disable.lists){
47343             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47344             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47345         }
47346         
47347         var ans = this.editorcore.getAllAncestors();
47348         if (this.formatCombo) {
47349             
47350             
47351             var store = this.formatCombo.store;
47352             this.formatCombo.setValue("");
47353             for (var i =0; i < ans.length;i++) {
47354                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47355                     // select it..
47356                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47357                     break;
47358                 }
47359             }
47360         }
47361         
47362         
47363         
47364         // hides menus... - so this cant be on a menu...
47365         Roo.menu.MenuMgr.hideAll();
47366
47367         //this.editorsyncValue();
47368     },
47369    
47370     
47371     createFontOptions : function(){
47372         var buf = [], fs = this.fontFamilies, ff, lc;
47373         
47374         
47375         
47376         for(var i = 0, len = fs.length; i< len; i++){
47377             ff = fs[i];
47378             lc = ff.toLowerCase();
47379             buf.push(
47380                 '<option value="',lc,'" style="font-family:',ff,';"',
47381                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47382                     ff,
47383                 '</option>'
47384             );
47385         }
47386         return buf.join('');
47387     },
47388     
47389     toggleSourceEdit : function(sourceEditMode){
47390         
47391         Roo.log("toolbar toogle");
47392         if(sourceEditMode === undefined){
47393             sourceEditMode = !this.sourceEditMode;
47394         }
47395         this.sourceEditMode = sourceEditMode === true;
47396         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47397         // just toggle the button?
47398         if(btn.pressed !== this.sourceEditMode){
47399             btn.toggle(this.sourceEditMode);
47400             return;
47401         }
47402         
47403         if(sourceEditMode){
47404             Roo.log("disabling buttons");
47405             this.tb.items.each(function(item){
47406                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47407                     item.disable();
47408                 }
47409             });
47410           
47411         }else{
47412             Roo.log("enabling buttons");
47413             if(this.editorcore.initialized){
47414                 this.tb.items.each(function(item){
47415                     item.enable();
47416                 });
47417             }
47418             
47419         }
47420         Roo.log("calling toggole on editor");
47421         // tell the editor that it's been pressed..
47422         this.editor.toggleSourceEdit(sourceEditMode);
47423        
47424     },
47425      /**
47426      * Object collection of toolbar tooltips for the buttons in the editor. The key
47427      * is the command id associated with that button and the value is a valid QuickTips object.
47428      * For example:
47429 <pre><code>
47430 {
47431     bold : {
47432         title: 'Bold (Ctrl+B)',
47433         text: 'Make the selected text bold.',
47434         cls: 'x-html-editor-tip'
47435     },
47436     italic : {
47437         title: 'Italic (Ctrl+I)',
47438         text: 'Make the selected text italic.',
47439         cls: 'x-html-editor-tip'
47440     },
47441     ...
47442 </code></pre>
47443     * @type Object
47444      */
47445     buttonTips : {
47446         bold : {
47447             title: 'Bold (Ctrl+B)',
47448             text: 'Make the selected text bold.',
47449             cls: 'x-html-editor-tip'
47450         },
47451         italic : {
47452             title: 'Italic (Ctrl+I)',
47453             text: 'Make the selected text italic.',
47454             cls: 'x-html-editor-tip'
47455         },
47456         underline : {
47457             title: 'Underline (Ctrl+U)',
47458             text: 'Underline the selected text.',
47459             cls: 'x-html-editor-tip'
47460         },
47461         strikethrough : {
47462             title: 'Strikethrough',
47463             text: 'Strikethrough the selected text.',
47464             cls: 'x-html-editor-tip'
47465         },
47466         increasefontsize : {
47467             title: 'Grow Text',
47468             text: 'Increase the font size.',
47469             cls: 'x-html-editor-tip'
47470         },
47471         decreasefontsize : {
47472             title: 'Shrink Text',
47473             text: 'Decrease the font size.',
47474             cls: 'x-html-editor-tip'
47475         },
47476         backcolor : {
47477             title: 'Text Highlight Color',
47478             text: 'Change the background color of the selected text.',
47479             cls: 'x-html-editor-tip'
47480         },
47481         forecolor : {
47482             title: 'Font Color',
47483             text: 'Change the color of the selected text.',
47484             cls: 'x-html-editor-tip'
47485         },
47486         justifyleft : {
47487             title: 'Align Text Left',
47488             text: 'Align text to the left.',
47489             cls: 'x-html-editor-tip'
47490         },
47491         justifycenter : {
47492             title: 'Center Text',
47493             text: 'Center text in the editor.',
47494             cls: 'x-html-editor-tip'
47495         },
47496         justifyright : {
47497             title: 'Align Text Right',
47498             text: 'Align text to the right.',
47499             cls: 'x-html-editor-tip'
47500         },
47501         insertunorderedlist : {
47502             title: 'Bullet List',
47503             text: 'Start a bulleted list.',
47504             cls: 'x-html-editor-tip'
47505         },
47506         insertorderedlist : {
47507             title: 'Numbered List',
47508             text: 'Start a numbered list.',
47509             cls: 'x-html-editor-tip'
47510         },
47511         createlink : {
47512             title: 'Hyperlink',
47513             text: 'Make the selected text a hyperlink.',
47514             cls: 'x-html-editor-tip'
47515         },
47516         sourceedit : {
47517             title: 'Source Edit',
47518             text: 'Switch to source editing mode.',
47519             cls: 'x-html-editor-tip'
47520         }
47521     },
47522     // private
47523     onDestroy : function(){
47524         if(this.rendered){
47525             
47526             this.tb.items.each(function(item){
47527                 if(item.menu){
47528                     item.menu.removeAll();
47529                     if(item.menu.el){
47530                         item.menu.el.destroy();
47531                     }
47532                 }
47533                 item.destroy();
47534             });
47535              
47536         }
47537     },
47538     onFirstFocus: function() {
47539         this.tb.items.each(function(item){
47540            item.enable();
47541         });
47542     }
47543 });
47544
47545
47546
47547
47548 // <script type="text/javascript">
47549 /*
47550  * Based on
47551  * Ext JS Library 1.1.1
47552  * Copyright(c) 2006-2007, Ext JS, LLC.
47553  *  
47554  
47555  */
47556
47557  
47558 /**
47559  * @class Roo.form.HtmlEditor.ToolbarContext
47560  * Context Toolbar
47561  * 
47562  * Usage:
47563  *
47564  new Roo.form.HtmlEditor({
47565     ....
47566     toolbars : [
47567         { xtype: 'ToolbarStandard', styles : {} }
47568         { xtype: 'ToolbarContext', disable : {} }
47569     ]
47570 })
47571
47572      
47573  * 
47574  * @config : {Object} disable List of elements to disable.. (not done yet.)
47575  * @config : {Object} styles  Map of styles available.
47576  * 
47577  */
47578
47579 Roo.form.HtmlEditor.ToolbarContext = function(config)
47580 {
47581     
47582     Roo.apply(this, config);
47583     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47584     // dont call parent... till later.
47585     this.styles = this.styles || {};
47586 }
47587
47588  
47589
47590 Roo.form.HtmlEditor.ToolbarContext.types = {
47591     'IMG' : {
47592         width : {
47593             title: "Width",
47594             width: 40
47595         },
47596         height:  {
47597             title: "Height",
47598             width: 40
47599         },
47600         align: {
47601             title: "Align",
47602             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47603             width : 80
47604             
47605         },
47606         border: {
47607             title: "Border",
47608             width: 40
47609         },
47610         alt: {
47611             title: "Alt",
47612             width: 120
47613         },
47614         src : {
47615             title: "Src",
47616             width: 220
47617         }
47618         
47619     },
47620     'A' : {
47621         name : {
47622             title: "Name",
47623             width: 50
47624         },
47625         target:  {
47626             title: "Target",
47627             width: 120
47628         },
47629         href:  {
47630             title: "Href",
47631             width: 220
47632         } // border?
47633         
47634     },
47635     'TABLE' : {
47636         rows : {
47637             title: "Rows",
47638             width: 20
47639         },
47640         cols : {
47641             title: "Cols",
47642             width: 20
47643         },
47644         width : {
47645             title: "Width",
47646             width: 40
47647         },
47648         height : {
47649             title: "Height",
47650             width: 40
47651         },
47652         border : {
47653             title: "Border",
47654             width: 20
47655         }
47656     },
47657     'TD' : {
47658         width : {
47659             title: "Width",
47660             width: 40
47661         },
47662         height : {
47663             title: "Height",
47664             width: 40
47665         },   
47666         align: {
47667             title: "Align",
47668             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
47669             width: 80
47670         },
47671         valign: {
47672             title: "Valign",
47673             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
47674             width: 80
47675         },
47676         colspan: {
47677             title: "Colspan",
47678             width: 20
47679             
47680         },
47681          'font-family'  : {
47682             title : "Font",
47683             style : 'fontFamily',
47684             displayField: 'display',
47685             optname : 'font-family',
47686             width: 140
47687         }
47688     },
47689     'INPUT' : {
47690         name : {
47691             title: "name",
47692             width: 120
47693         },
47694         value : {
47695             title: "Value",
47696             width: 120
47697         },
47698         width : {
47699             title: "Width",
47700             width: 40
47701         }
47702     },
47703     'LABEL' : {
47704         'for' : {
47705             title: "For",
47706             width: 120
47707         }
47708     },
47709     'TEXTAREA' : {
47710           name : {
47711             title: "name",
47712             width: 120
47713         },
47714         rows : {
47715             title: "Rows",
47716             width: 20
47717         },
47718         cols : {
47719             title: "Cols",
47720             width: 20
47721         }
47722     },
47723     'SELECT' : {
47724         name : {
47725             title: "name",
47726             width: 120
47727         },
47728         selectoptions : {
47729             title: "Options",
47730             width: 200
47731         }
47732     },
47733     
47734     // should we really allow this??
47735     // should this just be 
47736     'BODY' : {
47737         title : {
47738             title: "Title",
47739             width: 200,
47740             disabled : true
47741         }
47742     },
47743     'SPAN' : {
47744         'font-family'  : {
47745             title : "Font",
47746             style : 'fontFamily',
47747             displayField: 'display',
47748             optname : 'font-family',
47749             width: 140
47750         }
47751     },
47752     'DIV' : {
47753         'font-family'  : {
47754             title : "Font",
47755             style : 'fontFamily',
47756             displayField: 'display',
47757             optname : 'font-family',
47758             width: 140
47759         }
47760     },
47761      'P' : {
47762         'font-family'  : {
47763             title : "Font",
47764             style : 'fontFamily',
47765             displayField: 'display',
47766             optname : 'font-family',
47767             width: 140
47768         }
47769     },
47770     
47771     '*' : {
47772         // empty..
47773     }
47774
47775 };
47776
47777 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47778 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47779
47780 Roo.form.HtmlEditor.ToolbarContext.options = {
47781         'font-family'  : [ 
47782                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47783                 [ 'Courier New', 'Courier New'],
47784                 [ 'Tahoma', 'Tahoma'],
47785                 [ 'Times New Roman,serif', 'Times'],
47786                 [ 'Verdana','Verdana' ]
47787         ]
47788 };
47789
47790 // fixme - these need to be configurable..
47791  
47792
47793 //Roo.form.HtmlEditor.ToolbarContext.types
47794
47795
47796 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47797     
47798     tb: false,
47799     
47800     rendered: false,
47801     
47802     editor : false,
47803     editorcore : false,
47804     /**
47805      * @cfg {Object} disable  List of toolbar elements to disable
47806          
47807      */
47808     disable : false,
47809     /**
47810      * @cfg {Object} styles List of styles 
47811      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47812      *
47813      * These must be defined in the page, so they get rendered correctly..
47814      * .headline { }
47815      * TD.underline { }
47816      * 
47817      */
47818     styles : false,
47819     
47820     options: false,
47821     
47822     toolbars : false,
47823     
47824     init : function(editor)
47825     {
47826         this.editor = editor;
47827         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47828         var editorcore = this.editorcore;
47829         
47830         var fid = editorcore.frameId;
47831         var etb = this;
47832         function btn(id, toggle, handler){
47833             var xid = fid + '-'+ id ;
47834             return {
47835                 id : xid,
47836                 cmd : id,
47837                 cls : 'x-btn-icon x-edit-'+id,
47838                 enableToggle:toggle !== false,
47839                 scope: editorcore, // was editor...
47840                 handler:handler||editorcore.relayBtnCmd,
47841                 clickEvent:'mousedown',
47842                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47843                 tabIndex:-1
47844             };
47845         }
47846         // create a new element.
47847         var wdiv = editor.wrap.createChild({
47848                 tag: 'div'
47849             }, editor.wrap.dom.firstChild.nextSibling, true);
47850         
47851         // can we do this more than once??
47852         
47853          // stop form submits
47854       
47855  
47856         // disable everything...
47857         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47858         this.toolbars = {};
47859            
47860         for (var i in  ty) {
47861           
47862             this.toolbars[i] = this.buildToolbar(ty[i],i);
47863         }
47864         this.tb = this.toolbars.BODY;
47865         this.tb.el.show();
47866         this.buildFooter();
47867         this.footer.show();
47868         editor.on('hide', function( ) { this.footer.hide() }, this);
47869         editor.on('show', function( ) { this.footer.show() }, this);
47870         
47871          
47872         this.rendered = true;
47873         
47874         // the all the btns;
47875         editor.on('editorevent', this.updateToolbar, this);
47876         // other toolbars need to implement this..
47877         //editor.on('editmodechange', this.updateToolbar, this);
47878     },
47879     
47880     
47881     
47882     /**
47883      * Protected method that will not generally be called directly. It triggers
47884      * a toolbar update by reading the markup state of the current selection in the editor.
47885      *
47886      * Note you can force an update by calling on('editorevent', scope, false)
47887      */
47888     updateToolbar: function(editor,ev,sel){
47889
47890         //Roo.log(ev);
47891         // capture mouse up - this is handy for selecting images..
47892         // perhaps should go somewhere else...
47893         if(!this.editorcore.activated){
47894              this.editor.onFirstFocus();
47895             return;
47896         }
47897         
47898         
47899         
47900         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47901         // selectNode - might want to handle IE?
47902         if (ev &&
47903             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47904             ev.target && ev.target.tagName == 'IMG') {
47905             // they have click on an image...
47906             // let's see if we can change the selection...
47907             sel = ev.target;
47908          
47909               var nodeRange = sel.ownerDocument.createRange();
47910             try {
47911                 nodeRange.selectNode(sel);
47912             } catch (e) {
47913                 nodeRange.selectNodeContents(sel);
47914             }
47915             //nodeRange.collapse(true);
47916             var s = this.editorcore.win.getSelection();
47917             s.removeAllRanges();
47918             s.addRange(nodeRange);
47919         }  
47920         
47921       
47922         var updateFooter = sel ? false : true;
47923         
47924         
47925         var ans = this.editorcore.getAllAncestors();
47926         
47927         // pick
47928         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47929         
47930         if (!sel) { 
47931             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47932             sel = sel ? sel : this.editorcore.doc.body;
47933             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47934             
47935         }
47936         // pick a menu that exists..
47937         var tn = sel.tagName.toUpperCase();
47938         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47939         
47940         tn = sel.tagName.toUpperCase();
47941         
47942         var lastSel = this.tb.selectedNode;
47943         
47944         this.tb.selectedNode = sel;
47945         
47946         // if current menu does not match..
47947         
47948         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47949                 
47950             this.tb.el.hide();
47951             ///console.log("show: " + tn);
47952             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47953             this.tb.el.show();
47954             // update name
47955             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47956             
47957             
47958             // update attributes
47959             if (this.tb.fields) {
47960                 this.tb.fields.each(function(e) {
47961                     if (e.stylename) {
47962                         e.setValue(sel.style[e.stylename]);
47963                         return;
47964                     } 
47965                    e.setValue(sel.getAttribute(e.attrname));
47966                 });
47967             }
47968             
47969             var hasStyles = false;
47970             for(var i in this.styles) {
47971                 hasStyles = true;
47972                 break;
47973             }
47974             
47975             // update styles
47976             if (hasStyles) { 
47977                 var st = this.tb.fields.item(0);
47978                 
47979                 st.store.removeAll();
47980                
47981                 
47982                 var cn = sel.className.split(/\s+/);
47983                 
47984                 var avs = [];
47985                 if (this.styles['*']) {
47986                     
47987                     Roo.each(this.styles['*'], function(v) {
47988                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47989                     });
47990                 }
47991                 if (this.styles[tn]) { 
47992                     Roo.each(this.styles[tn], function(v) {
47993                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47994                     });
47995                 }
47996                 
47997                 st.store.loadData(avs);
47998                 st.collapse();
47999                 st.setValue(cn);
48000             }
48001             // flag our selected Node.
48002             this.tb.selectedNode = sel;
48003            
48004            
48005             Roo.menu.MenuMgr.hideAll();
48006
48007         }
48008         
48009         if (!updateFooter) {
48010             //this.footDisp.dom.innerHTML = ''; 
48011             return;
48012         }
48013         // update the footer
48014         //
48015         var html = '';
48016         
48017         this.footerEls = ans.reverse();
48018         Roo.each(this.footerEls, function(a,i) {
48019             if (!a) { return; }
48020             html += html.length ? ' &gt; '  :  '';
48021             
48022             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48023             
48024         });
48025        
48026         // 
48027         var sz = this.footDisp.up('td').getSize();
48028         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48029         this.footDisp.dom.style.marginLeft = '5px';
48030         
48031         this.footDisp.dom.style.overflow = 'hidden';
48032         
48033         this.footDisp.dom.innerHTML = html;
48034             
48035         //this.editorsyncValue();
48036     },
48037      
48038     
48039    
48040        
48041     // private
48042     onDestroy : function(){
48043         if(this.rendered){
48044             
48045             this.tb.items.each(function(item){
48046                 if(item.menu){
48047                     item.menu.removeAll();
48048                     if(item.menu.el){
48049                         item.menu.el.destroy();
48050                     }
48051                 }
48052                 item.destroy();
48053             });
48054              
48055         }
48056     },
48057     onFirstFocus: function() {
48058         // need to do this for all the toolbars..
48059         this.tb.items.each(function(item){
48060            item.enable();
48061         });
48062     },
48063     buildToolbar: function(tlist, nm)
48064     {
48065         var editor = this.editor;
48066         var editorcore = this.editorcore;
48067          // create a new element.
48068         var wdiv = editor.wrap.createChild({
48069                 tag: 'div'
48070             }, editor.wrap.dom.firstChild.nextSibling, true);
48071         
48072        
48073         var tb = new Roo.Toolbar(wdiv);
48074         // add the name..
48075         
48076         tb.add(nm+ ":&nbsp;");
48077         
48078         var styles = [];
48079         for(var i in this.styles) {
48080             styles.push(i);
48081         }
48082         
48083         // styles...
48084         if (styles && styles.length) {
48085             
48086             // this needs a multi-select checkbox...
48087             tb.addField( new Roo.form.ComboBox({
48088                 store: new Roo.data.SimpleStore({
48089                     id : 'val',
48090                     fields: ['val', 'selected'],
48091                     data : [] 
48092                 }),
48093                 name : '-roo-edit-className',
48094                 attrname : 'className',
48095                 displayField: 'val',
48096                 typeAhead: false,
48097                 mode: 'local',
48098                 editable : false,
48099                 triggerAction: 'all',
48100                 emptyText:'Select Style',
48101                 selectOnFocus:true,
48102                 width: 130,
48103                 listeners : {
48104                     'select': function(c, r, i) {
48105                         // initial support only for on class per el..
48106                         tb.selectedNode.className =  r ? r.get('val') : '';
48107                         editorcore.syncValue();
48108                     }
48109                 }
48110     
48111             }));
48112         }
48113         
48114         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48115         var tbops = tbc.options;
48116         
48117         for (var i in tlist) {
48118             
48119             var item = tlist[i];
48120             tb.add(item.title + ":&nbsp;");
48121             
48122             
48123             //optname == used so you can configure the options available..
48124             var opts = item.opts ? item.opts : false;
48125             if (item.optname) {
48126                 opts = tbops[item.optname];
48127            
48128             }
48129             
48130             if (opts) {
48131                 // opts == pulldown..
48132                 tb.addField( new Roo.form.ComboBox({
48133                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48134                         id : 'val',
48135                         fields: ['val', 'display'],
48136                         data : opts  
48137                     }),
48138                     name : '-roo-edit-' + i,
48139                     attrname : i,
48140                     stylename : item.style ? item.style : false,
48141                     displayField: item.displayField ? item.displayField : 'val',
48142                     valueField :  'val',
48143                     typeAhead: false,
48144                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48145                     editable : false,
48146                     triggerAction: 'all',
48147                     emptyText:'Select',
48148                     selectOnFocus:true,
48149                     width: item.width ? item.width  : 130,
48150                     listeners : {
48151                         'select': function(c, r, i) {
48152                             if (c.stylename) {
48153                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48154                                 return;
48155                             }
48156                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48157                         }
48158                     }
48159
48160                 }));
48161                 continue;
48162                     
48163                  
48164                 
48165                 tb.addField( new Roo.form.TextField({
48166                     name: i,
48167                     width: 100,
48168                     //allowBlank:false,
48169                     value: ''
48170                 }));
48171                 continue;
48172             }
48173             tb.addField( new Roo.form.TextField({
48174                 name: '-roo-edit-' + i,
48175                 attrname : i,
48176                 
48177                 width: item.width,
48178                 //allowBlank:true,
48179                 value: '',
48180                 listeners: {
48181                     'change' : function(f, nv, ov) {
48182                         tb.selectedNode.setAttribute(f.attrname, nv);
48183                         editorcore.syncValue();
48184                     }
48185                 }
48186             }));
48187              
48188         }
48189         
48190         var _this = this;
48191         
48192         if(nm == 'BODY'){
48193             tb.addSeparator();
48194         
48195             tb.addButton( {
48196                 text: 'Stylesheets',
48197
48198                 listeners : {
48199                     click : function ()
48200                     {
48201                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48202                     }
48203                 }
48204             });
48205         }
48206         
48207         tb.addFill();
48208         tb.addButton( {
48209             text: 'Remove Tag',
48210     
48211             listeners : {
48212                 click : function ()
48213                 {
48214                     // remove
48215                     // undo does not work.
48216                      
48217                     var sn = tb.selectedNode;
48218                     
48219                     var pn = sn.parentNode;
48220                     
48221                     var stn =  sn.childNodes[0];
48222                     var en = sn.childNodes[sn.childNodes.length - 1 ];
48223                     while (sn.childNodes.length) {
48224                         var node = sn.childNodes[0];
48225                         sn.removeChild(node);
48226                         //Roo.log(node);
48227                         pn.insertBefore(node, sn);
48228                         
48229                     }
48230                     pn.removeChild(sn);
48231                     var range = editorcore.createRange();
48232         
48233                     range.setStart(stn,0);
48234                     range.setEnd(en,0); //????
48235                     //range.selectNode(sel);
48236                     
48237                     
48238                     var selection = editorcore.getSelection();
48239                     selection.removeAllRanges();
48240                     selection.addRange(range);
48241                     
48242                     
48243                     
48244                     //_this.updateToolbar(null, null, pn);
48245                     _this.updateToolbar(null, null, null);
48246                     _this.footDisp.dom.innerHTML = ''; 
48247                 }
48248             }
48249             
48250                     
48251                 
48252             
48253         });
48254         
48255         
48256         tb.el.on('click', function(e){
48257             e.preventDefault(); // what does this do?
48258         });
48259         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48260         tb.el.hide();
48261         tb.name = nm;
48262         // dont need to disable them... as they will get hidden
48263         return tb;
48264          
48265         
48266     },
48267     buildFooter : function()
48268     {
48269         
48270         var fel = this.editor.wrap.createChild();
48271         this.footer = new Roo.Toolbar(fel);
48272         // toolbar has scrolly on left / right?
48273         var footDisp= new Roo.Toolbar.Fill();
48274         var _t = this;
48275         this.footer.add(
48276             {
48277                 text : '&lt;',
48278                 xtype: 'Button',
48279                 handler : function() {
48280                     _t.footDisp.scrollTo('left',0,true)
48281                 }
48282             }
48283         );
48284         this.footer.add( footDisp );
48285         this.footer.add( 
48286             {
48287                 text : '&gt;',
48288                 xtype: 'Button',
48289                 handler : function() {
48290                     // no animation..
48291                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48292                 }
48293             }
48294         );
48295         var fel = Roo.get(footDisp.el);
48296         fel.addClass('x-editor-context');
48297         this.footDispWrap = fel; 
48298         this.footDispWrap.overflow  = 'hidden';
48299         
48300         this.footDisp = fel.createChild();
48301         this.footDispWrap.on('click', this.onContextClick, this)
48302         
48303         
48304     },
48305     onContextClick : function (ev,dom)
48306     {
48307         ev.preventDefault();
48308         var  cn = dom.className;
48309         //Roo.log(cn);
48310         if (!cn.match(/x-ed-loc-/)) {
48311             return;
48312         }
48313         var n = cn.split('-').pop();
48314         var ans = this.footerEls;
48315         var sel = ans[n];
48316         
48317          // pick
48318         var range = this.editorcore.createRange();
48319         
48320         range.selectNodeContents(sel);
48321         //range.selectNode(sel);
48322         
48323         
48324         var selection = this.editorcore.getSelection();
48325         selection.removeAllRanges();
48326         selection.addRange(range);
48327         
48328         
48329         
48330         this.updateToolbar(null, null, sel);
48331         
48332         
48333     }
48334     
48335     
48336     
48337     
48338     
48339 });
48340
48341
48342
48343
48344
48345 /*
48346  * Based on:
48347  * Ext JS Library 1.1.1
48348  * Copyright(c) 2006-2007, Ext JS, LLC.
48349  *
48350  * Originally Released Under LGPL - original licence link has changed is not relivant.
48351  *
48352  * Fork - LGPL
48353  * <script type="text/javascript">
48354  */
48355  
48356 /**
48357  * @class Roo.form.BasicForm
48358  * @extends Roo.util.Observable
48359  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48360  * @constructor
48361  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48362  * @param {Object} config Configuration options
48363  */
48364 Roo.form.BasicForm = function(el, config){
48365     this.allItems = [];
48366     this.childForms = [];
48367     Roo.apply(this, config);
48368     /*
48369      * The Roo.form.Field items in this form.
48370      * @type MixedCollection
48371      */
48372      
48373      
48374     this.items = new Roo.util.MixedCollection(false, function(o){
48375         return o.id || (o.id = Roo.id());
48376     });
48377     this.addEvents({
48378         /**
48379          * @event beforeaction
48380          * Fires before any action is performed. Return false to cancel the action.
48381          * @param {Form} this
48382          * @param {Action} action The action to be performed
48383          */
48384         beforeaction: true,
48385         /**
48386          * @event actionfailed
48387          * Fires when an action fails.
48388          * @param {Form} this
48389          * @param {Action} action The action that failed
48390          */
48391         actionfailed : true,
48392         /**
48393          * @event actioncomplete
48394          * Fires when an action is completed.
48395          * @param {Form} this
48396          * @param {Action} action The action that completed
48397          */
48398         actioncomplete : true
48399     });
48400     if(el){
48401         this.initEl(el);
48402     }
48403     Roo.form.BasicForm.superclass.constructor.call(this);
48404     
48405     Roo.form.BasicForm.popover.apply();
48406 };
48407
48408 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48409     /**
48410      * @cfg {String} method
48411      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48412      */
48413     /**
48414      * @cfg {DataReader} reader
48415      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48416      * This is optional as there is built-in support for processing JSON.
48417      */
48418     /**
48419      * @cfg {DataReader} errorReader
48420      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48421      * This is completely optional as there is built-in support for processing JSON.
48422      */
48423     /**
48424      * @cfg {String} url
48425      * The URL to use for form actions if one isn't supplied in the action options.
48426      */
48427     /**
48428      * @cfg {Boolean} fileUpload
48429      * Set to true if this form is a file upload.
48430      */
48431      
48432     /**
48433      * @cfg {Object} baseParams
48434      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48435      */
48436      /**
48437      
48438     /**
48439      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
48440      */
48441     timeout: 30,
48442
48443     // private
48444     activeAction : null,
48445
48446     /**
48447      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
48448      * or setValues() data instead of when the form was first created.
48449      */
48450     trackResetOnLoad : false,
48451     
48452     
48453     /**
48454      * childForms - used for multi-tab forms
48455      * @type {Array}
48456      */
48457     childForms : false,
48458     
48459     /**
48460      * allItems - full list of fields.
48461      * @type {Array}
48462      */
48463     allItems : false,
48464     
48465     /**
48466      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
48467      * element by passing it or its id or mask the form itself by passing in true.
48468      * @type Mixed
48469      */
48470     waitMsgTarget : false,
48471     
48472     /**
48473      * @type Boolean
48474      */
48475     disableMask : false,
48476     
48477     /**
48478      * @cfg {Boolean} errorMask (true|false) default false
48479      */
48480     errorMask : false,
48481     
48482     /**
48483      * @cfg {Number} maskOffset Default 100
48484      */
48485     maskOffset : 100,
48486
48487     // private
48488     initEl : function(el){
48489         this.el = Roo.get(el);
48490         this.id = this.el.id || Roo.id();
48491         this.el.on('submit', this.onSubmit, this);
48492         this.el.addClass('x-form');
48493     },
48494
48495     // private
48496     onSubmit : function(e){
48497         e.stopEvent();
48498     },
48499
48500     /**
48501      * Returns true if client-side validation on the form is successful.
48502      * @return Boolean
48503      */
48504     isValid : function(){
48505         var valid = true;
48506         var target = false;
48507         this.items.each(function(f){
48508             if(f.validate()){
48509                 return;
48510             }
48511             
48512             valid = false;
48513                 
48514             if(!target && f.el.isVisible(true)){
48515                 target = f;
48516             }
48517         });
48518         
48519         if(this.errorMask && !valid){
48520             Roo.form.BasicForm.popover.mask(this, target);
48521         }
48522         
48523         return valid;
48524     },
48525     /**
48526      * Returns array of invalid form fields.
48527      * @return Array
48528      */
48529     
48530     invalidFields : function()
48531     {
48532         var ret = [];
48533         this.items.each(function(f){
48534             if(f.validate()){
48535                 return;
48536             }
48537             ret.push(f);
48538             
48539         });
48540         
48541         return ret;
48542     },
48543     
48544     
48545     /**
48546      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
48547      * @return Boolean
48548      */
48549     isDirty : function(){
48550         var dirty = false;
48551         this.items.each(function(f){
48552            if(f.isDirty()){
48553                dirty = true;
48554                return false;
48555            }
48556         });
48557         return dirty;
48558     },
48559     
48560     /**
48561      * Returns true if any fields in this form have changed since their original load. (New version)
48562      * @return Boolean
48563      */
48564     
48565     hasChanged : function()
48566     {
48567         var dirty = false;
48568         this.items.each(function(f){
48569            if(f.hasChanged()){
48570                dirty = true;
48571                return false;
48572            }
48573         });
48574         return dirty;
48575         
48576     },
48577     /**
48578      * Resets all hasChanged to 'false' -
48579      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
48580      * So hasChanged storage is only to be used for this purpose
48581      * @return Boolean
48582      */
48583     resetHasChanged : function()
48584     {
48585         this.items.each(function(f){
48586            f.resetHasChanged();
48587         });
48588         
48589     },
48590     
48591     
48592     /**
48593      * Performs a predefined action (submit or load) or custom actions you define on this form.
48594      * @param {String} actionName The name of the action type
48595      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
48596      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
48597      * accept other config options):
48598      * <pre>
48599 Property          Type             Description
48600 ----------------  ---------------  ----------------------------------------------------------------------------------
48601 url               String           The url for the action (defaults to the form's url)
48602 method            String           The form method to use (defaults to the form's method, or POST if not defined)
48603 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
48604 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
48605                                    validate the form on the client (defaults to false)
48606      * </pre>
48607      * @return {BasicForm} this
48608      */
48609     doAction : function(action, options){
48610         if(typeof action == 'string'){
48611             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
48612         }
48613         if(this.fireEvent('beforeaction', this, action) !== false){
48614             this.beforeAction(action);
48615             action.run.defer(100, action);
48616         }
48617         return this;
48618     },
48619
48620     /**
48621      * Shortcut to do a submit action.
48622      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48623      * @return {BasicForm} this
48624      */
48625     submit : function(options){
48626         this.doAction('submit', options);
48627         return this;
48628     },
48629
48630     /**
48631      * Shortcut to do a load action.
48632      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48633      * @return {BasicForm} this
48634      */
48635     load : function(options){
48636         this.doAction('load', options);
48637         return this;
48638     },
48639
48640     /**
48641      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
48642      * @param {Record} record The record to edit
48643      * @return {BasicForm} this
48644      */
48645     updateRecord : function(record){
48646         record.beginEdit();
48647         var fs = record.fields;
48648         fs.each(function(f){
48649             var field = this.findField(f.name);
48650             if(field){
48651                 record.set(f.name, field.getValue());
48652             }
48653         }, this);
48654         record.endEdit();
48655         return this;
48656     },
48657
48658     /**
48659      * Loads an Roo.data.Record into this form.
48660      * @param {Record} record The record to load
48661      * @return {BasicForm} this
48662      */
48663     loadRecord : function(record){
48664         this.setValues(record.data);
48665         return this;
48666     },
48667
48668     // private
48669     beforeAction : function(action){
48670         var o = action.options;
48671         
48672         if(!this.disableMask) {
48673             if(this.waitMsgTarget === true){
48674                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
48675             }else if(this.waitMsgTarget){
48676                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
48677                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
48678             }else {
48679                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
48680             }
48681         }
48682         
48683          
48684     },
48685
48686     // private
48687     afterAction : function(action, success){
48688         this.activeAction = null;
48689         var o = action.options;
48690         
48691         if(!this.disableMask) {
48692             if(this.waitMsgTarget === true){
48693                 this.el.unmask();
48694             }else if(this.waitMsgTarget){
48695                 this.waitMsgTarget.unmask();
48696             }else{
48697                 Roo.MessageBox.updateProgress(1);
48698                 Roo.MessageBox.hide();
48699             }
48700         }
48701         
48702         if(success){
48703             if(o.reset){
48704                 this.reset();
48705             }
48706             Roo.callback(o.success, o.scope, [this, action]);
48707             this.fireEvent('actioncomplete', this, action);
48708             
48709         }else{
48710             
48711             // failure condition..
48712             // we have a scenario where updates need confirming.
48713             // eg. if a locking scenario exists..
48714             // we look for { errors : { needs_confirm : true }} in the response.
48715             if (
48716                 (typeof(action.result) != 'undefined')  &&
48717                 (typeof(action.result.errors) != 'undefined')  &&
48718                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48719            ){
48720                 var _t = this;
48721                 Roo.MessageBox.confirm(
48722                     "Change requires confirmation",
48723                     action.result.errorMsg,
48724                     function(r) {
48725                         if (r != 'yes') {
48726                             return;
48727                         }
48728                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48729                     }
48730                     
48731                 );
48732                 
48733                 
48734                 
48735                 return;
48736             }
48737             
48738             Roo.callback(o.failure, o.scope, [this, action]);
48739             // show an error message if no failed handler is set..
48740             if (!this.hasListener('actionfailed')) {
48741                 Roo.MessageBox.alert("Error",
48742                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48743                         action.result.errorMsg :
48744                         "Saving Failed, please check your entries or try again"
48745                 );
48746             }
48747             
48748             this.fireEvent('actionfailed', this, action);
48749         }
48750         
48751     },
48752
48753     /**
48754      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48755      * @param {String} id The value to search for
48756      * @return Field
48757      */
48758     findField : function(id){
48759         var field = this.items.get(id);
48760         if(!field){
48761             this.items.each(function(f){
48762                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48763                     field = f;
48764                     return false;
48765                 }
48766             });
48767         }
48768         return field || null;
48769     },
48770
48771     /**
48772      * Add a secondary form to this one, 
48773      * Used to provide tabbed forms. One form is primary, with hidden values 
48774      * which mirror the elements from the other forms.
48775      * 
48776      * @param {Roo.form.Form} form to add.
48777      * 
48778      */
48779     addForm : function(form)
48780     {
48781        
48782         if (this.childForms.indexOf(form) > -1) {
48783             // already added..
48784             return;
48785         }
48786         this.childForms.push(form);
48787         var n = '';
48788         Roo.each(form.allItems, function (fe) {
48789             
48790             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48791             if (this.findField(n)) { // already added..
48792                 return;
48793             }
48794             var add = new Roo.form.Hidden({
48795                 name : n
48796             });
48797             add.render(this.el);
48798             
48799             this.add( add );
48800         }, this);
48801         
48802     },
48803     /**
48804      * Mark fields in this form invalid in bulk.
48805      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48806      * @return {BasicForm} this
48807      */
48808     markInvalid : function(errors){
48809         if(errors instanceof Array){
48810             for(var i = 0, len = errors.length; i < len; i++){
48811                 var fieldError = errors[i];
48812                 var f = this.findField(fieldError.id);
48813                 if(f){
48814                     f.markInvalid(fieldError.msg);
48815                 }
48816             }
48817         }else{
48818             var field, id;
48819             for(id in errors){
48820                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48821                     field.markInvalid(errors[id]);
48822                 }
48823             }
48824         }
48825         Roo.each(this.childForms || [], function (f) {
48826             f.markInvalid(errors);
48827         });
48828         
48829         return this;
48830     },
48831
48832     /**
48833      * Set values for fields in this form in bulk.
48834      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48835      * @return {BasicForm} this
48836      */
48837     setValues : function(values){
48838         if(values instanceof Array){ // array of objects
48839             for(var i = 0, len = values.length; i < len; i++){
48840                 var v = values[i];
48841                 var f = this.findField(v.id);
48842                 if(f){
48843                     f.setValue(v.value);
48844                     if(this.trackResetOnLoad){
48845                         f.originalValue = f.getValue();
48846                     }
48847                 }
48848             }
48849         }else{ // object hash
48850             var field, id;
48851             for(id in values){
48852                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48853                     
48854                     if (field.setFromData && 
48855                         field.valueField && 
48856                         field.displayField &&
48857                         // combos' with local stores can 
48858                         // be queried via setValue()
48859                         // to set their value..
48860                         (field.store && !field.store.isLocal)
48861                         ) {
48862                         // it's a combo
48863                         var sd = { };
48864                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48865                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48866                         field.setFromData(sd);
48867                         
48868                     } else {
48869                         field.setValue(values[id]);
48870                     }
48871                     
48872                     
48873                     if(this.trackResetOnLoad){
48874                         field.originalValue = field.getValue();
48875                     }
48876                 }
48877             }
48878         }
48879         this.resetHasChanged();
48880         
48881         
48882         Roo.each(this.childForms || [], function (f) {
48883             f.setValues(values);
48884             f.resetHasChanged();
48885         });
48886                 
48887         return this;
48888     },
48889  
48890     /**
48891      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48892      * they are returned as an array.
48893      * @param {Boolean} asString
48894      * @return {Object}
48895      */
48896     getValues : function(asString){
48897         if (this.childForms) {
48898             // copy values from the child forms
48899             Roo.each(this.childForms, function (f) {
48900                 this.setValues(f.getValues());
48901             }, this);
48902         }
48903         
48904         // use formdata
48905         if (typeof(FormData) != 'undefined' && asString !== true) {
48906             // this relies on a 'recent' version of chrome apparently...
48907             try {
48908                 var fd = (new FormData(this.el.dom)).entries();
48909                 var ret = {};
48910                 var ent = fd.next();
48911                 while (!ent.done) {
48912                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48913                     ent = fd.next();
48914                 };
48915                 return ret;
48916             } catch(e) {
48917                 
48918             }
48919             
48920         }
48921         
48922         
48923         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48924         if(asString === true){
48925             return fs;
48926         }
48927         return Roo.urlDecode(fs);
48928     },
48929     
48930     /**
48931      * Returns the fields in this form as an object with key/value pairs. 
48932      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48933      * @return {Object}
48934      */
48935     getFieldValues : function(with_hidden)
48936     {
48937         if (this.childForms) {
48938             // copy values from the child forms
48939             // should this call getFieldValues - probably not as we do not currently copy
48940             // hidden fields when we generate..
48941             Roo.each(this.childForms, function (f) {
48942                 this.setValues(f.getValues());
48943             }, this);
48944         }
48945         
48946         var ret = {};
48947         this.items.each(function(f){
48948             if (!f.getName()) {
48949                 return;
48950             }
48951             var v = f.getValue();
48952             if (f.inputType =='radio') {
48953                 if (typeof(ret[f.getName()]) == 'undefined') {
48954                     ret[f.getName()] = ''; // empty..
48955                 }
48956                 
48957                 if (!f.el.dom.checked) {
48958                     return;
48959                     
48960                 }
48961                 v = f.el.dom.value;
48962                 
48963             }
48964             
48965             // not sure if this supported any more..
48966             if ((typeof(v) == 'object') && f.getRawValue) {
48967                 v = f.getRawValue() ; // dates..
48968             }
48969             // combo boxes where name != hiddenName...
48970             if (f.name != f.getName()) {
48971                 ret[f.name] = f.getRawValue();
48972             }
48973             ret[f.getName()] = v;
48974         });
48975         
48976         return ret;
48977     },
48978
48979     /**
48980      * Clears all invalid messages in this form.
48981      * @return {BasicForm} this
48982      */
48983     clearInvalid : function(){
48984         this.items.each(function(f){
48985            f.clearInvalid();
48986         });
48987         
48988         Roo.each(this.childForms || [], function (f) {
48989             f.clearInvalid();
48990         });
48991         
48992         
48993         return this;
48994     },
48995
48996     /**
48997      * Resets this form.
48998      * @return {BasicForm} this
48999      */
49000     reset : function(){
49001         this.items.each(function(f){
49002             f.reset();
49003         });
49004         
49005         Roo.each(this.childForms || [], function (f) {
49006             f.reset();
49007         });
49008         this.resetHasChanged();
49009         
49010         return this;
49011     },
49012
49013     /**
49014      * Add Roo.form components to this form.
49015      * @param {Field} field1
49016      * @param {Field} field2 (optional)
49017      * @param {Field} etc (optional)
49018      * @return {BasicForm} this
49019      */
49020     add : function(){
49021         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49022         return this;
49023     },
49024
49025
49026     /**
49027      * Removes a field from the items collection (does NOT remove its markup).
49028      * @param {Field} field
49029      * @return {BasicForm} this
49030      */
49031     remove : function(field){
49032         this.items.remove(field);
49033         return this;
49034     },
49035
49036     /**
49037      * Looks at the fields in this form, checks them for an id attribute,
49038      * and calls applyTo on the existing dom element with that id.
49039      * @return {BasicForm} this
49040      */
49041     render : function(){
49042         this.items.each(function(f){
49043             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49044                 f.applyTo(f.id);
49045             }
49046         });
49047         return this;
49048     },
49049
49050     /**
49051      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49052      * @param {Object} values
49053      * @return {BasicForm} this
49054      */
49055     applyToFields : function(o){
49056         this.items.each(function(f){
49057            Roo.apply(f, o);
49058         });
49059         return this;
49060     },
49061
49062     /**
49063      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49064      * @param {Object} values
49065      * @return {BasicForm} this
49066      */
49067     applyIfToFields : function(o){
49068         this.items.each(function(f){
49069            Roo.applyIf(f, o);
49070         });
49071         return this;
49072     }
49073 });
49074
49075 // back compat
49076 Roo.BasicForm = Roo.form.BasicForm;
49077
49078 Roo.apply(Roo.form.BasicForm, {
49079     
49080     popover : {
49081         
49082         padding : 5,
49083         
49084         isApplied : false,
49085         
49086         isMasked : false,
49087         
49088         form : false,
49089         
49090         target : false,
49091         
49092         intervalID : false,
49093         
49094         maskEl : false,
49095         
49096         apply : function()
49097         {
49098             if(this.isApplied){
49099                 return;
49100             }
49101             
49102             this.maskEl = {
49103                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49104                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49105                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49106                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49107             };
49108             
49109             this.maskEl.top.enableDisplayMode("block");
49110             this.maskEl.left.enableDisplayMode("block");
49111             this.maskEl.bottom.enableDisplayMode("block");
49112             this.maskEl.right.enableDisplayMode("block");
49113             
49114             Roo.get(document.body).on('click', function(){
49115                 this.unmask();
49116             }, this);
49117             
49118             Roo.get(document.body).on('touchstart', function(){
49119                 this.unmask();
49120             }, this);
49121             
49122             this.isApplied = true
49123         },
49124         
49125         mask : function(form, target)
49126         {
49127             this.form = form;
49128             
49129             this.target = target;
49130             
49131             if(!this.form.errorMask || !target.el){
49132                 return;
49133             }
49134             
49135             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49136             
49137             var ot = this.target.el.calcOffsetsTo(scrollable);
49138             
49139             var scrollTo = ot[1] - this.form.maskOffset;
49140             
49141             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49142             
49143             scrollable.scrollTo('top', scrollTo);
49144             
49145             var el = this.target.wrap || this.target.el;
49146             
49147             var box = el.getBox();
49148             
49149             this.maskEl.top.setStyle('position', 'absolute');
49150             this.maskEl.top.setStyle('z-index', 10000);
49151             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49152             this.maskEl.top.setLeft(0);
49153             this.maskEl.top.setTop(0);
49154             this.maskEl.top.show();
49155             
49156             this.maskEl.left.setStyle('position', 'absolute');
49157             this.maskEl.left.setStyle('z-index', 10000);
49158             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49159             this.maskEl.left.setLeft(0);
49160             this.maskEl.left.setTop(box.y - this.padding);
49161             this.maskEl.left.show();
49162
49163             this.maskEl.bottom.setStyle('position', 'absolute');
49164             this.maskEl.bottom.setStyle('z-index', 10000);
49165             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49166             this.maskEl.bottom.setLeft(0);
49167             this.maskEl.bottom.setTop(box.bottom + this.padding);
49168             this.maskEl.bottom.show();
49169
49170             this.maskEl.right.setStyle('position', 'absolute');
49171             this.maskEl.right.setStyle('z-index', 10000);
49172             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49173             this.maskEl.right.setLeft(box.right + this.padding);
49174             this.maskEl.right.setTop(box.y - this.padding);
49175             this.maskEl.right.show();
49176
49177             this.intervalID = window.setInterval(function() {
49178                 Roo.form.BasicForm.popover.unmask();
49179             }, 10000);
49180
49181             window.onwheel = function(){ return false;};
49182             
49183             (function(){ this.isMasked = true; }).defer(500, this);
49184             
49185         },
49186         
49187         unmask : function()
49188         {
49189             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49190                 return;
49191             }
49192             
49193             this.maskEl.top.setStyle('position', 'absolute');
49194             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49195             this.maskEl.top.hide();
49196
49197             this.maskEl.left.setStyle('position', 'absolute');
49198             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49199             this.maskEl.left.hide();
49200
49201             this.maskEl.bottom.setStyle('position', 'absolute');
49202             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49203             this.maskEl.bottom.hide();
49204
49205             this.maskEl.right.setStyle('position', 'absolute');
49206             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49207             this.maskEl.right.hide();
49208             
49209             window.onwheel = function(){ return true;};
49210             
49211             if(this.intervalID){
49212                 window.clearInterval(this.intervalID);
49213                 this.intervalID = false;
49214             }
49215             
49216             this.isMasked = false;
49217             
49218         }
49219         
49220     }
49221     
49222 });/*
49223  * Based on:
49224  * Ext JS Library 1.1.1
49225  * Copyright(c) 2006-2007, Ext JS, LLC.
49226  *
49227  * Originally Released Under LGPL - original licence link has changed is not relivant.
49228  *
49229  * Fork - LGPL
49230  * <script type="text/javascript">
49231  */
49232
49233 /**
49234  * @class Roo.form.Form
49235  * @extends Roo.form.BasicForm
49236  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49237  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49238  * @constructor
49239  * @param {Object} config Configuration options
49240  */
49241 Roo.form.Form = function(config){
49242     var xitems =  [];
49243     if (config.items) {
49244         xitems = config.items;
49245         delete config.items;
49246     }
49247    
49248     
49249     Roo.form.Form.superclass.constructor.call(this, null, config);
49250     this.url = this.url || this.action;
49251     if(!this.root){
49252         this.root = new Roo.form.Layout(Roo.applyIf({
49253             id: Roo.id()
49254         }, config));
49255     }
49256     this.active = this.root;
49257     /**
49258      * Array of all the buttons that have been added to this form via {@link addButton}
49259      * @type Array
49260      */
49261     this.buttons = [];
49262     this.allItems = [];
49263     this.addEvents({
49264         /**
49265          * @event clientvalidation
49266          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49267          * @param {Form} this
49268          * @param {Boolean} valid true if the form has passed client-side validation
49269          */
49270         clientvalidation: true,
49271         /**
49272          * @event rendered
49273          * Fires when the form is rendered
49274          * @param {Roo.form.Form} form
49275          */
49276         rendered : true
49277     });
49278     
49279     if (this.progressUrl) {
49280             // push a hidden field onto the list of fields..
49281             this.addxtype( {
49282                     xns: Roo.form, 
49283                     xtype : 'Hidden', 
49284                     name : 'UPLOAD_IDENTIFIER' 
49285             });
49286         }
49287         
49288     
49289     Roo.each(xitems, this.addxtype, this);
49290     
49291 };
49292
49293 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49294      /**
49295      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49296      */
49297     
49298     /**
49299      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49300      */
49301     /**
49302      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49303      */
49304     /**
49305      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49306      */
49307     buttonAlign:'center',
49308
49309     /**
49310      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49311      */
49312     minButtonWidth:75,
49313
49314     /**
49315      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49316      * This property cascades to child containers if not set.
49317      */
49318     labelAlign:'left',
49319
49320     /**
49321      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49322      * fires a looping event with that state. This is required to bind buttons to the valid
49323      * state using the config value formBind:true on the button.
49324      */
49325     monitorValid : false,
49326
49327     /**
49328      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49329      */
49330     monitorPoll : 200,
49331     
49332     /**
49333      * @cfg {String} progressUrl - Url to return progress data 
49334      */
49335     
49336     progressUrl : false,
49337     /**
49338      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49339      * sending a formdata with extra parameters - eg uploaded elements.
49340      */
49341     
49342     formData : false,
49343     
49344     /**
49345      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49346      * fields are added and the column is closed. If no fields are passed the column remains open
49347      * until end() is called.
49348      * @param {Object} config The config to pass to the column
49349      * @param {Field} field1 (optional)
49350      * @param {Field} field2 (optional)
49351      * @param {Field} etc (optional)
49352      * @return Column The column container object
49353      */
49354     column : function(c){
49355         var col = new Roo.form.Column(c);
49356         this.start(col);
49357         if(arguments.length > 1){ // duplicate code required because of Opera
49358             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49359             this.end();
49360         }
49361         return col;
49362     },
49363
49364     /**
49365      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49366      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49367      * until end() is called.
49368      * @param {Object} config The config to pass to the fieldset
49369      * @param {Field} field1 (optional)
49370      * @param {Field} field2 (optional)
49371      * @param {Field} etc (optional)
49372      * @return FieldSet The fieldset container object
49373      */
49374     fieldset : function(c){
49375         var fs = new Roo.form.FieldSet(c);
49376         this.start(fs);
49377         if(arguments.length > 1){ // duplicate code required because of Opera
49378             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49379             this.end();
49380         }
49381         return fs;
49382     },
49383
49384     /**
49385      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49386      * fields are added and the container is closed. If no fields are passed the container remains open
49387      * until end() is called.
49388      * @param {Object} config The config to pass to the Layout
49389      * @param {Field} field1 (optional)
49390      * @param {Field} field2 (optional)
49391      * @param {Field} etc (optional)
49392      * @return Layout The container object
49393      */
49394     container : function(c){
49395         var l = new Roo.form.Layout(c);
49396         this.start(l);
49397         if(arguments.length > 1){ // duplicate code required because of Opera
49398             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49399             this.end();
49400         }
49401         return l;
49402     },
49403
49404     /**
49405      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49406      * @param {Object} container A Roo.form.Layout or subclass of Layout
49407      * @return {Form} this
49408      */
49409     start : function(c){
49410         // cascade label info
49411         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49412         this.active.stack.push(c);
49413         c.ownerCt = this.active;
49414         this.active = c;
49415         return this;
49416     },
49417
49418     /**
49419      * Closes the current open container
49420      * @return {Form} this
49421      */
49422     end : function(){
49423         if(this.active == this.root){
49424             return this;
49425         }
49426         this.active = this.active.ownerCt;
49427         return this;
49428     },
49429
49430     /**
49431      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49432      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49433      * as the label of the field.
49434      * @param {Field} field1
49435      * @param {Field} field2 (optional)
49436      * @param {Field} etc. (optional)
49437      * @return {Form} this
49438      */
49439     add : function(){
49440         this.active.stack.push.apply(this.active.stack, arguments);
49441         this.allItems.push.apply(this.allItems,arguments);
49442         var r = [];
49443         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
49444             if(a[i].isFormField){
49445                 r.push(a[i]);
49446             }
49447         }
49448         if(r.length > 0){
49449             Roo.form.Form.superclass.add.apply(this, r);
49450         }
49451         return this;
49452     },
49453     
49454
49455     
49456     
49457     
49458      /**
49459      * Find any element that has been added to a form, using it's ID or name
49460      * This can include framesets, columns etc. along with regular fields..
49461      * @param {String} id - id or name to find.
49462      
49463      * @return {Element} e - or false if nothing found.
49464      */
49465     findbyId : function(id)
49466     {
49467         var ret = false;
49468         if (!id) {
49469             return ret;
49470         }
49471         Roo.each(this.allItems, function(f){
49472             if (f.id == id || f.name == id ){
49473                 ret = f;
49474                 return false;
49475             }
49476         });
49477         return ret;
49478     },
49479
49480     
49481     
49482     /**
49483      * Render this form into the passed container. This should only be called once!
49484      * @param {String/HTMLElement/Element} container The element this component should be rendered into
49485      * @return {Form} this
49486      */
49487     render : function(ct)
49488     {
49489         
49490         
49491         
49492         ct = Roo.get(ct);
49493         var o = this.autoCreate || {
49494             tag: 'form',
49495             method : this.method || 'POST',
49496             id : this.id || Roo.id()
49497         };
49498         this.initEl(ct.createChild(o));
49499
49500         this.root.render(this.el);
49501         
49502        
49503              
49504         this.items.each(function(f){
49505             f.render('x-form-el-'+f.id);
49506         });
49507
49508         if(this.buttons.length > 0){
49509             // tables are required to maintain order and for correct IE layout
49510             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
49511                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
49512                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
49513             }}, null, true);
49514             var tr = tb.getElementsByTagName('tr')[0];
49515             for(var i = 0, len = this.buttons.length; i < len; i++) {
49516                 var b = this.buttons[i];
49517                 var td = document.createElement('td');
49518                 td.className = 'x-form-btn-td';
49519                 b.render(tr.appendChild(td));
49520             }
49521         }
49522         if(this.monitorValid){ // initialize after render
49523             this.startMonitoring();
49524         }
49525         this.fireEvent('rendered', this);
49526         return this;
49527     },
49528
49529     /**
49530      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
49531      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
49532      * object or a valid Roo.DomHelper element config
49533      * @param {Function} handler The function called when the button is clicked
49534      * @param {Object} scope (optional) The scope of the handler function
49535      * @return {Roo.Button}
49536      */
49537     addButton : function(config, handler, scope){
49538         var bc = {
49539             handler: handler,
49540             scope: scope,
49541             minWidth: this.minButtonWidth,
49542             hideParent:true
49543         };
49544         if(typeof config == "string"){
49545             bc.text = config;
49546         }else{
49547             Roo.apply(bc, config);
49548         }
49549         var btn = new Roo.Button(null, bc);
49550         this.buttons.push(btn);
49551         return btn;
49552     },
49553
49554      /**
49555      * Adds a series of form elements (using the xtype property as the factory method.
49556      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
49557      * @param {Object} config 
49558      */
49559     
49560     addxtype : function()
49561     {
49562         var ar = Array.prototype.slice.call(arguments, 0);
49563         var ret = false;
49564         for(var i = 0; i < ar.length; i++) {
49565             if (!ar[i]) {
49566                 continue; // skip -- if this happends something invalid got sent, we 
49567                 // should ignore it, as basically that interface element will not show up
49568                 // and that should be pretty obvious!!
49569             }
49570             
49571             if (Roo.form[ar[i].xtype]) {
49572                 ar[i].form = this;
49573                 var fe = Roo.factory(ar[i], Roo.form);
49574                 if (!ret) {
49575                     ret = fe;
49576                 }
49577                 fe.form = this;
49578                 if (fe.store) {
49579                     fe.store.form = this;
49580                 }
49581                 if (fe.isLayout) {  
49582                          
49583                     this.start(fe);
49584                     this.allItems.push(fe);
49585                     if (fe.items && fe.addxtype) {
49586                         fe.addxtype.apply(fe, fe.items);
49587                         delete fe.items;
49588                     }
49589                      this.end();
49590                     continue;
49591                 }
49592                 
49593                 
49594                  
49595                 this.add(fe);
49596               //  console.log('adding ' + ar[i].xtype);
49597             }
49598             if (ar[i].xtype == 'Button') {  
49599                 //console.log('adding button');
49600                 //console.log(ar[i]);
49601                 this.addButton(ar[i]);
49602                 this.allItems.push(fe);
49603                 continue;
49604             }
49605             
49606             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
49607                 alert('end is not supported on xtype any more, use items');
49608             //    this.end();
49609             //    //console.log('adding end');
49610             }
49611             
49612         }
49613         return ret;
49614     },
49615     
49616     /**
49617      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
49618      * option "monitorValid"
49619      */
49620     startMonitoring : function(){
49621         if(!this.bound){
49622             this.bound = true;
49623             Roo.TaskMgr.start({
49624                 run : this.bindHandler,
49625                 interval : this.monitorPoll || 200,
49626                 scope: this
49627             });
49628         }
49629     },
49630
49631     /**
49632      * Stops monitoring of the valid state of this form
49633      */
49634     stopMonitoring : function(){
49635         this.bound = false;
49636     },
49637
49638     // private
49639     bindHandler : function(){
49640         if(!this.bound){
49641             return false; // stops binding
49642         }
49643         var valid = true;
49644         this.items.each(function(f){
49645             if(!f.isValid(true)){
49646                 valid = false;
49647                 return false;
49648             }
49649         });
49650         for(var i = 0, len = this.buttons.length; i < len; i++){
49651             var btn = this.buttons[i];
49652             if(btn.formBind === true && btn.disabled === valid){
49653                 btn.setDisabled(!valid);
49654             }
49655         }
49656         this.fireEvent('clientvalidation', this, valid);
49657     }
49658     
49659     
49660     
49661     
49662     
49663     
49664     
49665     
49666 });
49667
49668
49669 // back compat
49670 Roo.Form = Roo.form.Form;
49671 /*
49672  * Based on:
49673  * Ext JS Library 1.1.1
49674  * Copyright(c) 2006-2007, Ext JS, LLC.
49675  *
49676  * Originally Released Under LGPL - original licence link has changed is not relivant.
49677  *
49678  * Fork - LGPL
49679  * <script type="text/javascript">
49680  */
49681
49682 // as we use this in bootstrap.
49683 Roo.namespace('Roo.form');
49684  /**
49685  * @class Roo.form.Action
49686  * Internal Class used to handle form actions
49687  * @constructor
49688  * @param {Roo.form.BasicForm} el The form element or its id
49689  * @param {Object} config Configuration options
49690  */
49691
49692  
49693  
49694 // define the action interface
49695 Roo.form.Action = function(form, options){
49696     this.form = form;
49697     this.options = options || {};
49698 };
49699 /**
49700  * Client Validation Failed
49701  * @const 
49702  */
49703 Roo.form.Action.CLIENT_INVALID = 'client';
49704 /**
49705  * Server Validation Failed
49706  * @const 
49707  */
49708 Roo.form.Action.SERVER_INVALID = 'server';
49709  /**
49710  * Connect to Server Failed
49711  * @const 
49712  */
49713 Roo.form.Action.CONNECT_FAILURE = 'connect';
49714 /**
49715  * Reading Data from Server Failed
49716  * @const 
49717  */
49718 Roo.form.Action.LOAD_FAILURE = 'load';
49719
49720 Roo.form.Action.prototype = {
49721     type : 'default',
49722     failureType : undefined,
49723     response : undefined,
49724     result : undefined,
49725
49726     // interface method
49727     run : function(options){
49728
49729     },
49730
49731     // interface method
49732     success : function(response){
49733
49734     },
49735
49736     // interface method
49737     handleResponse : function(response){
49738
49739     },
49740
49741     // default connection failure
49742     failure : function(response){
49743         
49744         this.response = response;
49745         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49746         this.form.afterAction(this, false);
49747     },
49748
49749     processResponse : function(response){
49750         this.response = response;
49751         if(!response.responseText){
49752             return true;
49753         }
49754         this.result = this.handleResponse(response);
49755         return this.result;
49756     },
49757
49758     // utility functions used internally
49759     getUrl : function(appendParams){
49760         var url = this.options.url || this.form.url || this.form.el.dom.action;
49761         if(appendParams){
49762             var p = this.getParams();
49763             if(p){
49764                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49765             }
49766         }
49767         return url;
49768     },
49769
49770     getMethod : function(){
49771         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49772     },
49773
49774     getParams : function(){
49775         var bp = this.form.baseParams;
49776         var p = this.options.params;
49777         if(p){
49778             if(typeof p == "object"){
49779                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49780             }else if(typeof p == 'string' && bp){
49781                 p += '&' + Roo.urlEncode(bp);
49782             }
49783         }else if(bp){
49784             p = Roo.urlEncode(bp);
49785         }
49786         return p;
49787     },
49788
49789     createCallback : function(){
49790         return {
49791             success: this.success,
49792             failure: this.failure,
49793             scope: this,
49794             timeout: (this.form.timeout*1000),
49795             upload: this.form.fileUpload ? this.success : undefined
49796         };
49797     }
49798 };
49799
49800 Roo.form.Action.Submit = function(form, options){
49801     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49802 };
49803
49804 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49805     type : 'submit',
49806
49807     haveProgress : false,
49808     uploadComplete : false,
49809     
49810     // uploadProgress indicator.
49811     uploadProgress : function()
49812     {
49813         if (!this.form.progressUrl) {
49814             return;
49815         }
49816         
49817         if (!this.haveProgress) {
49818             Roo.MessageBox.progress("Uploading", "Uploading");
49819         }
49820         if (this.uploadComplete) {
49821            Roo.MessageBox.hide();
49822            return;
49823         }
49824         
49825         this.haveProgress = true;
49826    
49827         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49828         
49829         var c = new Roo.data.Connection();
49830         c.request({
49831             url : this.form.progressUrl,
49832             params: {
49833                 id : uid
49834             },
49835             method: 'GET',
49836             success : function(req){
49837                //console.log(data);
49838                 var rdata = false;
49839                 var edata;
49840                 try  {
49841                    rdata = Roo.decode(req.responseText)
49842                 } catch (e) {
49843                     Roo.log("Invalid data from server..");
49844                     Roo.log(edata);
49845                     return;
49846                 }
49847                 if (!rdata || !rdata.success) {
49848                     Roo.log(rdata);
49849                     Roo.MessageBox.alert(Roo.encode(rdata));
49850                     return;
49851                 }
49852                 var data = rdata.data;
49853                 
49854                 if (this.uploadComplete) {
49855                    Roo.MessageBox.hide();
49856                    return;
49857                 }
49858                    
49859                 if (data){
49860                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49861                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49862                     );
49863                 }
49864                 this.uploadProgress.defer(2000,this);
49865             },
49866        
49867             failure: function(data) {
49868                 Roo.log('progress url failed ');
49869                 Roo.log(data);
49870             },
49871             scope : this
49872         });
49873            
49874     },
49875     
49876     
49877     run : function()
49878     {
49879         // run get Values on the form, so it syncs any secondary forms.
49880         this.form.getValues();
49881         
49882         var o = this.options;
49883         var method = this.getMethod();
49884         var isPost = method == 'POST';
49885         if(o.clientValidation === false || this.form.isValid()){
49886             
49887             if (this.form.progressUrl) {
49888                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49889                     (new Date() * 1) + '' + Math.random());
49890                     
49891             } 
49892             
49893             
49894             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49895                 form:this.form.el.dom,
49896                 url:this.getUrl(!isPost),
49897                 method: method,
49898                 params:isPost ? this.getParams() : null,
49899                 isUpload: this.form.fileUpload,
49900                 formData : this.form.formData
49901             }));
49902             
49903             this.uploadProgress();
49904
49905         }else if (o.clientValidation !== false){ // client validation failed
49906             this.failureType = Roo.form.Action.CLIENT_INVALID;
49907             this.form.afterAction(this, false);
49908         }
49909     },
49910
49911     success : function(response)
49912     {
49913         this.uploadComplete= true;
49914         if (this.haveProgress) {
49915             Roo.MessageBox.hide();
49916         }
49917         
49918         
49919         var result = this.processResponse(response);
49920         if(result === true || result.success){
49921             this.form.afterAction(this, true);
49922             return;
49923         }
49924         if(result.errors){
49925             this.form.markInvalid(result.errors);
49926             this.failureType = Roo.form.Action.SERVER_INVALID;
49927         }
49928         this.form.afterAction(this, false);
49929     },
49930     failure : function(response)
49931     {
49932         this.uploadComplete= true;
49933         if (this.haveProgress) {
49934             Roo.MessageBox.hide();
49935         }
49936         
49937         this.response = response;
49938         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49939         this.form.afterAction(this, false);
49940     },
49941     
49942     handleResponse : function(response){
49943         if(this.form.errorReader){
49944             var rs = this.form.errorReader.read(response);
49945             var errors = [];
49946             if(rs.records){
49947                 for(var i = 0, len = rs.records.length; i < len; i++) {
49948                     var r = rs.records[i];
49949                     errors[i] = r.data;
49950                 }
49951             }
49952             if(errors.length < 1){
49953                 errors = null;
49954             }
49955             return {
49956                 success : rs.success,
49957                 errors : errors
49958             };
49959         }
49960         var ret = false;
49961         try {
49962             ret = Roo.decode(response.responseText);
49963         } catch (e) {
49964             ret = {
49965                 success: false,
49966                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49967                 errors : []
49968             };
49969         }
49970         return ret;
49971         
49972     }
49973 });
49974
49975
49976 Roo.form.Action.Load = function(form, options){
49977     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49978     this.reader = this.form.reader;
49979 };
49980
49981 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49982     type : 'load',
49983
49984     run : function(){
49985         
49986         Roo.Ajax.request(Roo.apply(
49987                 this.createCallback(), {
49988                     method:this.getMethod(),
49989                     url:this.getUrl(false),
49990                     params:this.getParams()
49991         }));
49992     },
49993
49994     success : function(response){
49995         
49996         var result = this.processResponse(response);
49997         if(result === true || !result.success || !result.data){
49998             this.failureType = Roo.form.Action.LOAD_FAILURE;
49999             this.form.afterAction(this, false);
50000             return;
50001         }
50002         this.form.clearInvalid();
50003         this.form.setValues(result.data);
50004         this.form.afterAction(this, true);
50005     },
50006
50007     handleResponse : function(response){
50008         if(this.form.reader){
50009             var rs = this.form.reader.read(response);
50010             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50011             return {
50012                 success : rs.success,
50013                 data : data
50014             };
50015         }
50016         return Roo.decode(response.responseText);
50017     }
50018 });
50019
50020 Roo.form.Action.ACTION_TYPES = {
50021     'load' : Roo.form.Action.Load,
50022     'submit' : Roo.form.Action.Submit
50023 };/*
50024  * Based on:
50025  * Ext JS Library 1.1.1
50026  * Copyright(c) 2006-2007, Ext JS, LLC.
50027  *
50028  * Originally Released Under LGPL - original licence link has changed is not relivant.
50029  *
50030  * Fork - LGPL
50031  * <script type="text/javascript">
50032  */
50033  
50034 /**
50035  * @class Roo.form.Layout
50036  * @extends Roo.Component
50037  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50038  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50039  * @constructor
50040  * @param {Object} config Configuration options
50041  */
50042 Roo.form.Layout = function(config){
50043     var xitems = [];
50044     if (config.items) {
50045         xitems = config.items;
50046         delete config.items;
50047     }
50048     Roo.form.Layout.superclass.constructor.call(this, config);
50049     this.stack = [];
50050     Roo.each(xitems, this.addxtype, this);
50051      
50052 };
50053
50054 Roo.extend(Roo.form.Layout, Roo.Component, {
50055     /**
50056      * @cfg {String/Object} autoCreate
50057      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50058      */
50059     /**
50060      * @cfg {String/Object/Function} style
50061      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50062      * a function which returns such a specification.
50063      */
50064     /**
50065      * @cfg {String} labelAlign
50066      * Valid values are "left," "top" and "right" (defaults to "left")
50067      */
50068     /**
50069      * @cfg {Number} labelWidth
50070      * Fixed width in pixels of all field labels (defaults to undefined)
50071      */
50072     /**
50073      * @cfg {Boolean} clear
50074      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50075      */
50076     clear : true,
50077     /**
50078      * @cfg {String} labelSeparator
50079      * The separator to use after field labels (defaults to ':')
50080      */
50081     labelSeparator : ':',
50082     /**
50083      * @cfg {Boolean} hideLabels
50084      * True to suppress the display of field labels in this layout (defaults to false)
50085      */
50086     hideLabels : false,
50087
50088     // private
50089     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50090     
50091     isLayout : true,
50092     
50093     // private
50094     onRender : function(ct, position){
50095         if(this.el){ // from markup
50096             this.el = Roo.get(this.el);
50097         }else {  // generate
50098             var cfg = this.getAutoCreate();
50099             this.el = ct.createChild(cfg, position);
50100         }
50101         if(this.style){
50102             this.el.applyStyles(this.style);
50103         }
50104         if(this.labelAlign){
50105             this.el.addClass('x-form-label-'+this.labelAlign);
50106         }
50107         if(this.hideLabels){
50108             this.labelStyle = "display:none";
50109             this.elementStyle = "padding-left:0;";
50110         }else{
50111             if(typeof this.labelWidth == 'number'){
50112                 this.labelStyle = "width:"+this.labelWidth+"px;";
50113                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50114             }
50115             if(this.labelAlign == 'top'){
50116                 this.labelStyle = "width:auto;";
50117                 this.elementStyle = "padding-left:0;";
50118             }
50119         }
50120         var stack = this.stack;
50121         var slen = stack.length;
50122         if(slen > 0){
50123             if(!this.fieldTpl){
50124                 var t = new Roo.Template(
50125                     '<div class="x-form-item {5}">',
50126                         '<label for="{0}" style="{2}">{1}{4}</label>',
50127                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50128                         '</div>',
50129                     '</div><div class="x-form-clear-left"></div>'
50130                 );
50131                 t.disableFormats = true;
50132                 t.compile();
50133                 Roo.form.Layout.prototype.fieldTpl = t;
50134             }
50135             for(var i = 0; i < slen; i++) {
50136                 if(stack[i].isFormField){
50137                     this.renderField(stack[i]);
50138                 }else{
50139                     this.renderComponent(stack[i]);
50140                 }
50141             }
50142         }
50143         if(this.clear){
50144             this.el.createChild({cls:'x-form-clear'});
50145         }
50146     },
50147
50148     // private
50149     renderField : function(f){
50150         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50151                f.id, //0
50152                f.fieldLabel, //1
50153                f.labelStyle||this.labelStyle||'', //2
50154                this.elementStyle||'', //3
50155                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50156                f.itemCls||this.itemCls||''  //5
50157        ], true).getPrevSibling());
50158     },
50159
50160     // private
50161     renderComponent : function(c){
50162         c.render(c.isLayout ? this.el : this.el.createChild());    
50163     },
50164     /**
50165      * Adds a object form elements (using the xtype property as the factory method.)
50166      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50167      * @param {Object} config 
50168      */
50169     addxtype : function(o)
50170     {
50171         // create the lement.
50172         o.form = this.form;
50173         var fe = Roo.factory(o, Roo.form);
50174         this.form.allItems.push(fe);
50175         this.stack.push(fe);
50176         
50177         if (fe.isFormField) {
50178             this.form.items.add(fe);
50179         }
50180          
50181         return fe;
50182     }
50183 });
50184
50185 /**
50186  * @class Roo.form.Column
50187  * @extends Roo.form.Layout
50188  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50189  * @constructor
50190  * @param {Object} config Configuration options
50191  */
50192 Roo.form.Column = function(config){
50193     Roo.form.Column.superclass.constructor.call(this, config);
50194 };
50195
50196 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50197     /**
50198      * @cfg {Number/String} width
50199      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50200      */
50201     /**
50202      * @cfg {String/Object} autoCreate
50203      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50204      */
50205
50206     // private
50207     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50208
50209     // private
50210     onRender : function(ct, position){
50211         Roo.form.Column.superclass.onRender.call(this, ct, position);
50212         if(this.width){
50213             this.el.setWidth(this.width);
50214         }
50215     }
50216 });
50217
50218
50219 /**
50220  * @class Roo.form.Row
50221  * @extends Roo.form.Layout
50222  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50223  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50224  * @constructor
50225  * @param {Object} config Configuration options
50226  */
50227
50228  
50229 Roo.form.Row = function(config){
50230     Roo.form.Row.superclass.constructor.call(this, config);
50231 };
50232  
50233 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50234       /**
50235      * @cfg {Number/String} width
50236      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50237      */
50238     /**
50239      * @cfg {Number/String} height
50240      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50241      */
50242     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50243     
50244     padWidth : 20,
50245     // private
50246     onRender : function(ct, position){
50247         //console.log('row render');
50248         if(!this.rowTpl){
50249             var t = new Roo.Template(
50250                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50251                     '<label for="{0}" style="{2}">{1}{4}</label>',
50252                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50253                     '</div>',
50254                 '</div>'
50255             );
50256             t.disableFormats = true;
50257             t.compile();
50258             Roo.form.Layout.prototype.rowTpl = t;
50259         }
50260         this.fieldTpl = this.rowTpl;
50261         
50262         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50263         var labelWidth = 100;
50264         
50265         if ((this.labelAlign != 'top')) {
50266             if (typeof this.labelWidth == 'number') {
50267                 labelWidth = this.labelWidth
50268             }
50269             this.padWidth =  20 + labelWidth;
50270             
50271         }
50272         
50273         Roo.form.Column.superclass.onRender.call(this, ct, position);
50274         if(this.width){
50275             this.el.setWidth(this.width);
50276         }
50277         if(this.height){
50278             this.el.setHeight(this.height);
50279         }
50280     },
50281     
50282     // private
50283     renderField : function(f){
50284         f.fieldEl = this.fieldTpl.append(this.el, [
50285                f.id, f.fieldLabel,
50286                f.labelStyle||this.labelStyle||'',
50287                this.elementStyle||'',
50288                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50289                f.itemCls||this.itemCls||'',
50290                f.width ? f.width + this.padWidth : 160 + this.padWidth
50291        ],true);
50292     }
50293 });
50294  
50295
50296 /**
50297  * @class Roo.form.FieldSet
50298  * @extends Roo.form.Layout
50299  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50300  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50301  * @constructor
50302  * @param {Object} config Configuration options
50303  */
50304 Roo.form.FieldSet = function(config){
50305     Roo.form.FieldSet.superclass.constructor.call(this, config);
50306 };
50307
50308 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50309     /**
50310      * @cfg {String} legend
50311      * The text to display as the legend for the FieldSet (defaults to '')
50312      */
50313     /**
50314      * @cfg {String/Object} autoCreate
50315      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50316      */
50317
50318     // private
50319     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50320
50321     // private
50322     onRender : function(ct, position){
50323         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50324         if(this.legend){
50325             this.setLegend(this.legend);
50326         }
50327     },
50328
50329     // private
50330     setLegend : function(text){
50331         if(this.rendered){
50332             this.el.child('legend').update(text);
50333         }
50334     }
50335 });/*
50336  * Based on:
50337  * Ext JS Library 1.1.1
50338  * Copyright(c) 2006-2007, Ext JS, LLC.
50339  *
50340  * Originally Released Under LGPL - original licence link has changed is not relivant.
50341  *
50342  * Fork - LGPL
50343  * <script type="text/javascript">
50344  */
50345 /**
50346  * @class Roo.form.VTypes
50347  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50348  * @static
50349  */
50350 Roo.form.VTypes = function(){
50351     // closure these in so they are only created once.
50352     var alpha = /^[a-zA-Z_]+$/;
50353     var alphanum = /^[a-zA-Z0-9_]+$/;
50354     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50355     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50356
50357     // All these messages and functions are configurable
50358     return {
50359         /**
50360          * The function used to validate email addresses
50361          * @param {String} value The email address
50362          */
50363         'email' : function(v){
50364             return email.test(v);
50365         },
50366         /**
50367          * The error text to display when the email validation function returns false
50368          * @type String
50369          */
50370         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50371         /**
50372          * The keystroke filter mask to be applied on email input
50373          * @type RegExp
50374          */
50375         'emailMask' : /[a-z0-9_\.\-@]/i,
50376
50377         /**
50378          * The function used to validate URLs
50379          * @param {String} value The URL
50380          */
50381         'url' : function(v){
50382             return url.test(v);
50383         },
50384         /**
50385          * The error text to display when the url validation function returns false
50386          * @type String
50387          */
50388         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50389         
50390         /**
50391          * The function used to validate alpha values
50392          * @param {String} value The value
50393          */
50394         'alpha' : function(v){
50395             return alpha.test(v);
50396         },
50397         /**
50398          * The error text to display when the alpha validation function returns false
50399          * @type String
50400          */
50401         'alphaText' : 'This field should only contain letters and _',
50402         /**
50403          * The keystroke filter mask to be applied on alpha input
50404          * @type RegExp
50405          */
50406         'alphaMask' : /[a-z_]/i,
50407
50408         /**
50409          * The function used to validate alphanumeric values
50410          * @param {String} value The value
50411          */
50412         'alphanum' : function(v){
50413             return alphanum.test(v);
50414         },
50415         /**
50416          * The error text to display when the alphanumeric validation function returns false
50417          * @type String
50418          */
50419         'alphanumText' : 'This field should only contain letters, numbers and _',
50420         /**
50421          * The keystroke filter mask to be applied on alphanumeric input
50422          * @type RegExp
50423          */
50424         'alphanumMask' : /[a-z0-9_]/i
50425     };
50426 }();//<script type="text/javascript">
50427
50428 /**
50429  * @class Roo.form.FCKeditor
50430  * @extends Roo.form.TextArea
50431  * Wrapper around the FCKEditor http://www.fckeditor.net
50432  * @constructor
50433  * Creates a new FCKeditor
50434  * @param {Object} config Configuration options
50435  */
50436 Roo.form.FCKeditor = function(config){
50437     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50438     this.addEvents({
50439          /**
50440          * @event editorinit
50441          * Fired when the editor is initialized - you can add extra handlers here..
50442          * @param {FCKeditor} this
50443          * @param {Object} the FCK object.
50444          */
50445         editorinit : true
50446     });
50447     
50448     
50449 };
50450 Roo.form.FCKeditor.editors = { };
50451 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
50452 {
50453     //defaultAutoCreate : {
50454     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
50455     //},
50456     // private
50457     /**
50458      * @cfg {Object} fck options - see fck manual for details.
50459      */
50460     fckconfig : false,
50461     
50462     /**
50463      * @cfg {Object} fck toolbar set (Basic or Default)
50464      */
50465     toolbarSet : 'Basic',
50466     /**
50467      * @cfg {Object} fck BasePath
50468      */ 
50469     basePath : '/fckeditor/',
50470     
50471     
50472     frame : false,
50473     
50474     value : '',
50475     
50476    
50477     onRender : function(ct, position)
50478     {
50479         if(!this.el){
50480             this.defaultAutoCreate = {
50481                 tag: "textarea",
50482                 style:"width:300px;height:60px;",
50483                 autocomplete: "new-password"
50484             };
50485         }
50486         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
50487         /*
50488         if(this.grow){
50489             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
50490             if(this.preventScrollbars){
50491                 this.el.setStyle("overflow", "hidden");
50492             }
50493             this.el.setHeight(this.growMin);
50494         }
50495         */
50496         //console.log('onrender' + this.getId() );
50497         Roo.form.FCKeditor.editors[this.getId()] = this;
50498          
50499
50500         this.replaceTextarea() ;
50501         
50502     },
50503     
50504     getEditor : function() {
50505         return this.fckEditor;
50506     },
50507     /**
50508      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
50509      * @param {Mixed} value The value to set
50510      */
50511     
50512     
50513     setValue : function(value)
50514     {
50515         //console.log('setValue: ' + value);
50516         
50517         if(typeof(value) == 'undefined') { // not sure why this is happending...
50518             return;
50519         }
50520         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50521         
50522         //if(!this.el || !this.getEditor()) {
50523         //    this.value = value;
50524             //this.setValue.defer(100,this,[value]);    
50525         //    return;
50526         //} 
50527         
50528         if(!this.getEditor()) {
50529             return;
50530         }
50531         
50532         this.getEditor().SetData(value);
50533         
50534         //
50535
50536     },
50537
50538     /**
50539      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
50540      * @return {Mixed} value The field value
50541      */
50542     getValue : function()
50543     {
50544         
50545         if (this.frame && this.frame.dom.style.display == 'none') {
50546             return Roo.form.FCKeditor.superclass.getValue.call(this);
50547         }
50548         
50549         if(!this.el || !this.getEditor()) {
50550            
50551            // this.getValue.defer(100,this); 
50552             return this.value;
50553         }
50554        
50555         
50556         var value=this.getEditor().GetData();
50557         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50558         return Roo.form.FCKeditor.superclass.getValue.call(this);
50559         
50560
50561     },
50562
50563     /**
50564      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
50565      * @return {Mixed} value The field value
50566      */
50567     getRawValue : function()
50568     {
50569         if (this.frame && this.frame.dom.style.display == 'none') {
50570             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50571         }
50572         
50573         if(!this.el || !this.getEditor()) {
50574             //this.getRawValue.defer(100,this); 
50575             return this.value;
50576             return;
50577         }
50578         
50579         
50580         
50581         var value=this.getEditor().GetData();
50582         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
50583         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50584          
50585     },
50586     
50587     setSize : function(w,h) {
50588         
50589         
50590         
50591         //if (this.frame && this.frame.dom.style.display == 'none') {
50592         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50593         //    return;
50594         //}
50595         //if(!this.el || !this.getEditor()) {
50596         //    this.setSize.defer(100,this, [w,h]); 
50597         //    return;
50598         //}
50599         
50600         
50601         
50602         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50603         
50604         this.frame.dom.setAttribute('width', w);
50605         this.frame.dom.setAttribute('height', h);
50606         this.frame.setSize(w,h);
50607         
50608     },
50609     
50610     toggleSourceEdit : function(value) {
50611         
50612       
50613          
50614         this.el.dom.style.display = value ? '' : 'none';
50615         this.frame.dom.style.display = value ?  'none' : '';
50616         
50617     },
50618     
50619     
50620     focus: function(tag)
50621     {
50622         if (this.frame.dom.style.display == 'none') {
50623             return Roo.form.FCKeditor.superclass.focus.call(this);
50624         }
50625         if(!this.el || !this.getEditor()) {
50626             this.focus.defer(100,this, [tag]); 
50627             return;
50628         }
50629         
50630         
50631         
50632         
50633         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
50634         this.getEditor().Focus();
50635         if (tgs.length) {
50636             if (!this.getEditor().Selection.GetSelection()) {
50637                 this.focus.defer(100,this, [tag]); 
50638                 return;
50639             }
50640             
50641             
50642             var r = this.getEditor().EditorDocument.createRange();
50643             r.setStart(tgs[0],0);
50644             r.setEnd(tgs[0],0);
50645             this.getEditor().Selection.GetSelection().removeAllRanges();
50646             this.getEditor().Selection.GetSelection().addRange(r);
50647             this.getEditor().Focus();
50648         }
50649         
50650     },
50651     
50652     
50653     
50654     replaceTextarea : function()
50655     {
50656         if ( document.getElementById( this.getId() + '___Frame' ) ) {
50657             return ;
50658         }
50659         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
50660         //{
50661             // We must check the elements firstly using the Id and then the name.
50662         var oTextarea = document.getElementById( this.getId() );
50663         
50664         var colElementsByName = document.getElementsByName( this.getId() ) ;
50665          
50666         oTextarea.style.display = 'none' ;
50667
50668         if ( oTextarea.tabIndex ) {            
50669             this.TabIndex = oTextarea.tabIndex ;
50670         }
50671         
50672         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
50673         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
50674         this.frame = Roo.get(this.getId() + '___Frame')
50675     },
50676     
50677     _getConfigHtml : function()
50678     {
50679         var sConfig = '' ;
50680
50681         for ( var o in this.fckconfig ) {
50682             sConfig += sConfig.length > 0  ? '&amp;' : '';
50683             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
50684         }
50685
50686         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
50687     },
50688     
50689     
50690     _getIFrameHtml : function()
50691     {
50692         var sFile = 'fckeditor.html' ;
50693         /* no idea what this is about..
50694         try
50695         {
50696             if ( (/fcksource=true/i).test( window.top.location.search ) )
50697                 sFile = 'fckeditor.original.html' ;
50698         }
50699         catch (e) { 
50700         */
50701
50702         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
50703         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
50704         
50705         
50706         var html = '<iframe id="' + this.getId() +
50707             '___Frame" src="' + sLink +
50708             '" width="' + this.width +
50709             '" height="' + this.height + '"' +
50710             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
50711             ' frameborder="0" scrolling="no"></iframe>' ;
50712
50713         return html ;
50714     },
50715     
50716     _insertHtmlBefore : function( html, element )
50717     {
50718         if ( element.insertAdjacentHTML )       {
50719             // IE
50720             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50721         } else { // Gecko
50722             var oRange = document.createRange() ;
50723             oRange.setStartBefore( element ) ;
50724             var oFragment = oRange.createContextualFragment( html );
50725             element.parentNode.insertBefore( oFragment, element ) ;
50726         }
50727     }
50728     
50729     
50730   
50731     
50732     
50733     
50734     
50735
50736 });
50737
50738 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50739
50740 function FCKeditor_OnComplete(editorInstance){
50741     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50742     f.fckEditor = editorInstance;
50743     //console.log("loaded");
50744     f.fireEvent('editorinit', f, editorInstance);
50745
50746   
50747
50748  
50749
50750
50751
50752
50753
50754
50755
50756
50757
50758
50759
50760
50761
50762
50763
50764 //<script type="text/javascript">
50765 /**
50766  * @class Roo.form.GridField
50767  * @extends Roo.form.Field
50768  * Embed a grid (or editable grid into a form)
50769  * STATUS ALPHA
50770  * 
50771  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50772  * it needs 
50773  * xgrid.store = Roo.data.Store
50774  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50775  * xgrid.store.reader = Roo.data.JsonReader 
50776  * 
50777  * 
50778  * @constructor
50779  * Creates a new GridField
50780  * @param {Object} config Configuration options
50781  */
50782 Roo.form.GridField = function(config){
50783     Roo.form.GridField.superclass.constructor.call(this, config);
50784      
50785 };
50786
50787 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50788     /**
50789      * @cfg {Number} width  - used to restrict width of grid..
50790      */
50791     width : 100,
50792     /**
50793      * @cfg {Number} height - used to restrict height of grid..
50794      */
50795     height : 50,
50796      /**
50797      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50798          * 
50799          *}
50800      */
50801     xgrid : false, 
50802     /**
50803      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50804      * {tag: "input", type: "checkbox", autocomplete: "off"})
50805      */
50806    // defaultAutoCreate : { tag: 'div' },
50807     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50808     /**
50809      * @cfg {String} addTitle Text to include for adding a title.
50810      */
50811     addTitle : false,
50812     //
50813     onResize : function(){
50814         Roo.form.Field.superclass.onResize.apply(this, arguments);
50815     },
50816
50817     initEvents : function(){
50818         // Roo.form.Checkbox.superclass.initEvents.call(this);
50819         // has no events...
50820        
50821     },
50822
50823
50824     getResizeEl : function(){
50825         return this.wrap;
50826     },
50827
50828     getPositionEl : function(){
50829         return this.wrap;
50830     },
50831
50832     // private
50833     onRender : function(ct, position){
50834         
50835         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50836         var style = this.style;
50837         delete this.style;
50838         
50839         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50840         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50841         this.viewEl = this.wrap.createChild({ tag: 'div' });
50842         if (style) {
50843             this.viewEl.applyStyles(style);
50844         }
50845         if (this.width) {
50846             this.viewEl.setWidth(this.width);
50847         }
50848         if (this.height) {
50849             this.viewEl.setHeight(this.height);
50850         }
50851         //if(this.inputValue !== undefined){
50852         //this.setValue(this.value);
50853         
50854         
50855         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50856         
50857         
50858         this.grid.render();
50859         this.grid.getDataSource().on('remove', this.refreshValue, this);
50860         this.grid.getDataSource().on('update', this.refreshValue, this);
50861         this.grid.on('afteredit', this.refreshValue, this);
50862  
50863     },
50864      
50865     
50866     /**
50867      * Sets the value of the item. 
50868      * @param {String} either an object  or a string..
50869      */
50870     setValue : function(v){
50871         //this.value = v;
50872         v = v || []; // empty set..
50873         // this does not seem smart - it really only affects memoryproxy grids..
50874         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50875             var ds = this.grid.getDataSource();
50876             // assumes a json reader..
50877             var data = {}
50878             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50879             ds.loadData( data);
50880         }
50881         // clear selection so it does not get stale.
50882         if (this.grid.sm) { 
50883             this.grid.sm.clearSelections();
50884         }
50885         
50886         Roo.form.GridField.superclass.setValue.call(this, v);
50887         this.refreshValue();
50888         // should load data in the grid really....
50889     },
50890     
50891     // private
50892     refreshValue: function() {
50893          var val = [];
50894         this.grid.getDataSource().each(function(r) {
50895             val.push(r.data);
50896         });
50897         this.el.dom.value = Roo.encode(val);
50898     }
50899     
50900      
50901     
50902     
50903 });/*
50904  * Based on:
50905  * Ext JS Library 1.1.1
50906  * Copyright(c) 2006-2007, Ext JS, LLC.
50907  *
50908  * Originally Released Under LGPL - original licence link has changed is not relivant.
50909  *
50910  * Fork - LGPL
50911  * <script type="text/javascript">
50912  */
50913 /**
50914  * @class Roo.form.DisplayField
50915  * @extends Roo.form.Field
50916  * A generic Field to display non-editable data.
50917  * @cfg {Boolean} closable (true|false) default false
50918  * @constructor
50919  * Creates a new Display Field item.
50920  * @param {Object} config Configuration options
50921  */
50922 Roo.form.DisplayField = function(config){
50923     Roo.form.DisplayField.superclass.constructor.call(this, config);
50924     
50925     this.addEvents({
50926         /**
50927          * @event close
50928          * Fires after the click the close btn
50929              * @param {Roo.form.DisplayField} this
50930              */
50931         close : true
50932     });
50933 };
50934
50935 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50936     inputType:      'hidden',
50937     allowBlank:     true,
50938     readOnly:         true,
50939     
50940  
50941     /**
50942      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50943      */
50944     focusClass : undefined,
50945     /**
50946      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50947      */
50948     fieldClass: 'x-form-field',
50949     
50950      /**
50951      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50952      */
50953     valueRenderer: undefined,
50954     
50955     width: 100,
50956     /**
50957      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50958      * {tag: "input", type: "checkbox", autocomplete: "off"})
50959      */
50960      
50961  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50962  
50963     closable : false,
50964     
50965     onResize : function(){
50966         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50967         
50968     },
50969
50970     initEvents : function(){
50971         // Roo.form.Checkbox.superclass.initEvents.call(this);
50972         // has no events...
50973         
50974         if(this.closable){
50975             this.closeEl.on('click', this.onClose, this);
50976         }
50977        
50978     },
50979
50980
50981     getResizeEl : function(){
50982         return this.wrap;
50983     },
50984
50985     getPositionEl : function(){
50986         return this.wrap;
50987     },
50988
50989     // private
50990     onRender : function(ct, position){
50991         
50992         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50993         //if(this.inputValue !== undefined){
50994         this.wrap = this.el.wrap();
50995         
50996         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50997         
50998         if(this.closable){
50999             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51000         }
51001         
51002         if (this.bodyStyle) {
51003             this.viewEl.applyStyles(this.bodyStyle);
51004         }
51005         //this.viewEl.setStyle('padding', '2px');
51006         
51007         this.setValue(this.value);
51008         
51009     },
51010 /*
51011     // private
51012     initValue : Roo.emptyFn,
51013
51014   */
51015
51016         // private
51017     onClick : function(){
51018         
51019     },
51020
51021     /**
51022      * Sets the checked state of the checkbox.
51023      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51024      */
51025     setValue : function(v){
51026         this.value = v;
51027         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51028         // this might be called before we have a dom element..
51029         if (!this.viewEl) {
51030             return;
51031         }
51032         this.viewEl.dom.innerHTML = html;
51033         Roo.form.DisplayField.superclass.setValue.call(this, v);
51034
51035     },
51036     
51037     onClose : function(e)
51038     {
51039         e.preventDefault();
51040         
51041         this.fireEvent('close', this);
51042     }
51043 });/*
51044  * 
51045  * Licence- LGPL
51046  * 
51047  */
51048
51049 /**
51050  * @class Roo.form.DayPicker
51051  * @extends Roo.form.Field
51052  * A Day picker show [M] [T] [W] ....
51053  * @constructor
51054  * Creates a new Day Picker
51055  * @param {Object} config Configuration options
51056  */
51057 Roo.form.DayPicker= function(config){
51058     Roo.form.DayPicker.superclass.constructor.call(this, config);
51059      
51060 };
51061
51062 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51063     /**
51064      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51065      */
51066     focusClass : undefined,
51067     /**
51068      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51069      */
51070     fieldClass: "x-form-field",
51071    
51072     /**
51073      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51074      * {tag: "input", type: "checkbox", autocomplete: "off"})
51075      */
51076     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51077     
51078    
51079     actionMode : 'viewEl', 
51080     //
51081     // private
51082  
51083     inputType : 'hidden',
51084     
51085      
51086     inputElement: false, // real input element?
51087     basedOn: false, // ????
51088     
51089     isFormField: true, // not sure where this is needed!!!!
51090
51091     onResize : function(){
51092         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51093         if(!this.boxLabel){
51094             this.el.alignTo(this.wrap, 'c-c');
51095         }
51096     },
51097
51098     initEvents : function(){
51099         Roo.form.Checkbox.superclass.initEvents.call(this);
51100         this.el.on("click", this.onClick,  this);
51101         this.el.on("change", this.onClick,  this);
51102     },
51103
51104
51105     getResizeEl : function(){
51106         return this.wrap;
51107     },
51108
51109     getPositionEl : function(){
51110         return this.wrap;
51111     },
51112
51113     
51114     // private
51115     onRender : function(ct, position){
51116         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51117        
51118         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51119         
51120         var r1 = '<table><tr>';
51121         var r2 = '<tr class="x-form-daypick-icons">';
51122         for (var i=0; i < 7; i++) {
51123             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51124             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51125         }
51126         
51127         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51128         viewEl.select('img').on('click', this.onClick, this);
51129         this.viewEl = viewEl;   
51130         
51131         
51132         // this will not work on Chrome!!!
51133         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51134         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51135         
51136         
51137           
51138
51139     },
51140
51141     // private
51142     initValue : Roo.emptyFn,
51143
51144     /**
51145      * Returns the checked state of the checkbox.
51146      * @return {Boolean} True if checked, else false
51147      */
51148     getValue : function(){
51149         return this.el.dom.value;
51150         
51151     },
51152
51153         // private
51154     onClick : function(e){ 
51155         //this.setChecked(!this.checked);
51156         Roo.get(e.target).toggleClass('x-menu-item-checked');
51157         this.refreshValue();
51158         //if(this.el.dom.checked != this.checked){
51159         //    this.setValue(this.el.dom.checked);
51160        // }
51161     },
51162     
51163     // private
51164     refreshValue : function()
51165     {
51166         var val = '';
51167         this.viewEl.select('img',true).each(function(e,i,n)  {
51168             val += e.is(".x-menu-item-checked") ? String(n) : '';
51169         });
51170         this.setValue(val, true);
51171     },
51172
51173     /**
51174      * Sets the checked state of the checkbox.
51175      * On is always based on a string comparison between inputValue and the param.
51176      * @param {Boolean/String} value - the value to set 
51177      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51178      */
51179     setValue : function(v,suppressEvent){
51180         if (!this.el.dom) {
51181             return;
51182         }
51183         var old = this.el.dom.value ;
51184         this.el.dom.value = v;
51185         if (suppressEvent) {
51186             return ;
51187         }
51188          
51189         // update display..
51190         this.viewEl.select('img',true).each(function(e,i,n)  {
51191             
51192             var on = e.is(".x-menu-item-checked");
51193             var newv = v.indexOf(String(n)) > -1;
51194             if (on != newv) {
51195                 e.toggleClass('x-menu-item-checked');
51196             }
51197             
51198         });
51199         
51200         
51201         this.fireEvent('change', this, v, old);
51202         
51203         
51204     },
51205    
51206     // handle setting of hidden value by some other method!!?!?
51207     setFromHidden: function()
51208     {
51209         if(!this.el){
51210             return;
51211         }
51212         //console.log("SET FROM HIDDEN");
51213         //alert('setFrom hidden');
51214         this.setValue(this.el.dom.value);
51215     },
51216     
51217     onDestroy : function()
51218     {
51219         if(this.viewEl){
51220             Roo.get(this.viewEl).remove();
51221         }
51222          
51223         Roo.form.DayPicker.superclass.onDestroy.call(this);
51224     }
51225
51226 });/*
51227  * RooJS Library 1.1.1
51228  * Copyright(c) 2008-2011  Alan Knowles
51229  *
51230  * License - LGPL
51231  */
51232  
51233
51234 /**
51235  * @class Roo.form.ComboCheck
51236  * @extends Roo.form.ComboBox
51237  * A combobox for multiple select items.
51238  *
51239  * FIXME - could do with a reset button..
51240  * 
51241  * @constructor
51242  * Create a new ComboCheck
51243  * @param {Object} config Configuration options
51244  */
51245 Roo.form.ComboCheck = function(config){
51246     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51247     // should verify some data...
51248     // like
51249     // hiddenName = required..
51250     // displayField = required
51251     // valudField == required
51252     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51253     var _t = this;
51254     Roo.each(req, function(e) {
51255         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51256             throw "Roo.form.ComboCheck : missing value for: " + e;
51257         }
51258     });
51259     
51260     
51261 };
51262
51263 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51264      
51265      
51266     editable : false,
51267      
51268     selectedClass: 'x-menu-item-checked', 
51269     
51270     // private
51271     onRender : function(ct, position){
51272         var _t = this;
51273         
51274         
51275         
51276         if(!this.tpl){
51277             var cls = 'x-combo-list';
51278
51279             
51280             this.tpl =  new Roo.Template({
51281                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51282                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51283                    '<span>{' + this.displayField + '}</span>' +
51284                     '</div>' 
51285                 
51286             });
51287         }
51288  
51289         
51290         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51291         this.view.singleSelect = false;
51292         this.view.multiSelect = true;
51293         this.view.toggleSelect = true;
51294         this.pageTb.add(new Roo.Toolbar.Fill(), {
51295             
51296             text: 'Done',
51297             handler: function()
51298             {
51299                 _t.collapse();
51300             }
51301         });
51302     },
51303     
51304     onViewOver : function(e, t){
51305         // do nothing...
51306         return;
51307         
51308     },
51309     
51310     onViewClick : function(doFocus,index){
51311         return;
51312         
51313     },
51314     select: function () {
51315         //Roo.log("SELECT CALLED");
51316     },
51317      
51318     selectByValue : function(xv, scrollIntoView){
51319         var ar = this.getValueArray();
51320         var sels = [];
51321         
51322         Roo.each(ar, function(v) {
51323             if(v === undefined || v === null){
51324                 return;
51325             }
51326             var r = this.findRecord(this.valueField, v);
51327             if(r){
51328                 sels.push(this.store.indexOf(r))
51329                 
51330             }
51331         },this);
51332         this.view.select(sels);
51333         return false;
51334     },
51335     
51336     
51337     
51338     onSelect : function(record, index){
51339        // Roo.log("onselect Called");
51340        // this is only called by the clear button now..
51341         this.view.clearSelections();
51342         this.setValue('[]');
51343         if (this.value != this.valueBefore) {
51344             this.fireEvent('change', this, this.value, this.valueBefore);
51345             this.valueBefore = this.value;
51346         }
51347     },
51348     getValueArray : function()
51349     {
51350         var ar = [] ;
51351         
51352         try {
51353             //Roo.log(this.value);
51354             if (typeof(this.value) == 'undefined') {
51355                 return [];
51356             }
51357             var ar = Roo.decode(this.value);
51358             return  ar instanceof Array ? ar : []; //?? valid?
51359             
51360         } catch(e) {
51361             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51362             return [];
51363         }
51364          
51365     },
51366     expand : function ()
51367     {
51368         
51369         Roo.form.ComboCheck.superclass.expand.call(this);
51370         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51371         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51372         
51373
51374     },
51375     
51376     collapse : function(){
51377         Roo.form.ComboCheck.superclass.collapse.call(this);
51378         var sl = this.view.getSelectedIndexes();
51379         var st = this.store;
51380         var nv = [];
51381         var tv = [];
51382         var r;
51383         Roo.each(sl, function(i) {
51384             r = st.getAt(i);
51385             nv.push(r.get(this.valueField));
51386         },this);
51387         this.setValue(Roo.encode(nv));
51388         if (this.value != this.valueBefore) {
51389
51390             this.fireEvent('change', this, this.value, this.valueBefore);
51391             this.valueBefore = this.value;
51392         }
51393         
51394     },
51395     
51396     setValue : function(v){
51397         // Roo.log(v);
51398         this.value = v;
51399         
51400         var vals = this.getValueArray();
51401         var tv = [];
51402         Roo.each(vals, function(k) {
51403             var r = this.findRecord(this.valueField, k);
51404             if(r){
51405                 tv.push(r.data[this.displayField]);
51406             }else if(this.valueNotFoundText !== undefined){
51407                 tv.push( this.valueNotFoundText );
51408             }
51409         },this);
51410        // Roo.log(tv);
51411         
51412         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51413         this.hiddenField.value = v;
51414         this.value = v;
51415     }
51416     
51417 });/*
51418  * Based on:
51419  * Ext JS Library 1.1.1
51420  * Copyright(c) 2006-2007, Ext JS, LLC.
51421  *
51422  * Originally Released Under LGPL - original licence link has changed is not relivant.
51423  *
51424  * Fork - LGPL
51425  * <script type="text/javascript">
51426  */
51427  
51428 /**
51429  * @class Roo.form.Signature
51430  * @extends Roo.form.Field
51431  * Signature field.  
51432  * @constructor
51433  * 
51434  * @param {Object} config Configuration options
51435  */
51436
51437 Roo.form.Signature = function(config){
51438     Roo.form.Signature.superclass.constructor.call(this, config);
51439     
51440     this.addEvents({// not in used??
51441          /**
51442          * @event confirm
51443          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
51444              * @param {Roo.form.Signature} combo This combo box
51445              */
51446         'confirm' : true,
51447         /**
51448          * @event reset
51449          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
51450              * @param {Roo.form.ComboBox} combo This combo box
51451              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
51452              */
51453         'reset' : true
51454     });
51455 };
51456
51457 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
51458     /**
51459      * @cfg {Object} labels Label to use when rendering a form.
51460      * defaults to 
51461      * labels : { 
51462      *      clear : "Clear",
51463      *      confirm : "Confirm"
51464      *  }
51465      */
51466     labels : { 
51467         clear : "Clear",
51468         confirm : "Confirm"
51469     },
51470     /**
51471      * @cfg {Number} width The signature panel width (defaults to 300)
51472      */
51473     width: 300,
51474     /**
51475      * @cfg {Number} height The signature panel height (defaults to 100)
51476      */
51477     height : 100,
51478     /**
51479      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
51480      */
51481     allowBlank : false,
51482     
51483     //private
51484     // {Object} signPanel The signature SVG panel element (defaults to {})
51485     signPanel : {},
51486     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
51487     isMouseDown : false,
51488     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
51489     isConfirmed : false,
51490     // {String} signatureTmp SVG mapping string (defaults to empty string)
51491     signatureTmp : '',
51492     
51493     
51494     defaultAutoCreate : { // modified by initCompnoent..
51495         tag: "input",
51496         type:"hidden"
51497     },
51498
51499     // private
51500     onRender : function(ct, position){
51501         
51502         Roo.form.Signature.superclass.onRender.call(this, ct, position);
51503         
51504         this.wrap = this.el.wrap({
51505             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
51506         });
51507         
51508         this.createToolbar(this);
51509         this.signPanel = this.wrap.createChild({
51510                 tag: 'div',
51511                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
51512             }, this.el
51513         );
51514             
51515         this.svgID = Roo.id();
51516         this.svgEl = this.signPanel.createChild({
51517               xmlns : 'http://www.w3.org/2000/svg',
51518               tag : 'svg',
51519               id : this.svgID + "-svg",
51520               width: this.width,
51521               height: this.height,
51522               viewBox: '0 0 '+this.width+' '+this.height,
51523               cn : [
51524                 {
51525                     tag: "rect",
51526                     id: this.svgID + "-svg-r",
51527                     width: this.width,
51528                     height: this.height,
51529                     fill: "#ffa"
51530                 },
51531                 {
51532                     tag: "line",
51533                     id: this.svgID + "-svg-l",
51534                     x1: "0", // start
51535                     y1: (this.height*0.8), // start set the line in 80% of height
51536                     x2: this.width, // end
51537                     y2: (this.height*0.8), // end set the line in 80% of height
51538                     'stroke': "#666",
51539                     'stroke-width': "1",
51540                     'stroke-dasharray': "3",
51541                     'shape-rendering': "crispEdges",
51542                     'pointer-events': "none"
51543                 },
51544                 {
51545                     tag: "path",
51546                     id: this.svgID + "-svg-p",
51547                     'stroke': "navy",
51548                     'stroke-width': "3",
51549                     'fill': "none",
51550                     'pointer-events': 'none'
51551                 }
51552               ]
51553         });
51554         this.createSVG();
51555         this.svgBox = this.svgEl.dom.getScreenCTM();
51556     },
51557     createSVG : function(){ 
51558         var svg = this.signPanel;
51559         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
51560         var t = this;
51561
51562         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
51563         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
51564         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
51565         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
51566         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
51567         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
51568         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
51569         
51570     },
51571     isTouchEvent : function(e){
51572         return e.type.match(/^touch/);
51573     },
51574     getCoords : function (e) {
51575         var pt    = this.svgEl.dom.createSVGPoint();
51576         pt.x = e.clientX; 
51577         pt.y = e.clientY;
51578         if (this.isTouchEvent(e)) {
51579             pt.x =  e.targetTouches[0].clientX;
51580             pt.y = e.targetTouches[0].clientY;
51581         }
51582         var a = this.svgEl.dom.getScreenCTM();
51583         var b = a.inverse();
51584         var mx = pt.matrixTransform(b);
51585         return mx.x + ',' + mx.y;
51586     },
51587     //mouse event headler 
51588     down : function (e) {
51589         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
51590         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
51591         
51592         this.isMouseDown = true;
51593         
51594         e.preventDefault();
51595     },
51596     move : function (e) {
51597         if (this.isMouseDown) {
51598             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
51599             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
51600         }
51601         
51602         e.preventDefault();
51603     },
51604     up : function (e) {
51605         this.isMouseDown = false;
51606         var sp = this.signatureTmp.split(' ');
51607         
51608         if(sp.length > 1){
51609             if(!sp[sp.length-2].match(/^L/)){
51610                 sp.pop();
51611                 sp.pop();
51612                 sp.push("");
51613                 this.signatureTmp = sp.join(" ");
51614             }
51615         }
51616         if(this.getValue() != this.signatureTmp){
51617             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51618             this.isConfirmed = false;
51619         }
51620         e.preventDefault();
51621     },
51622     
51623     /**
51624      * Protected method that will not generally be called directly. It
51625      * is called when the editor creates its toolbar. Override this method if you need to
51626      * add custom toolbar buttons.
51627      * @param {HtmlEditor} editor
51628      */
51629     createToolbar : function(editor){
51630          function btn(id, toggle, handler){
51631             var xid = fid + '-'+ id ;
51632             return {
51633                 id : xid,
51634                 cmd : id,
51635                 cls : 'x-btn-icon x-edit-'+id,
51636                 enableToggle:toggle !== false,
51637                 scope: editor, // was editor...
51638                 handler:handler||editor.relayBtnCmd,
51639                 clickEvent:'mousedown',
51640                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51641                 tabIndex:-1
51642             };
51643         }
51644         
51645         
51646         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51647         this.tb = tb;
51648         this.tb.add(
51649            {
51650                 cls : ' x-signature-btn x-signature-'+id,
51651                 scope: editor, // was editor...
51652                 handler: this.reset,
51653                 clickEvent:'mousedown',
51654                 text: this.labels.clear
51655             },
51656             {
51657                  xtype : 'Fill',
51658                  xns: Roo.Toolbar
51659             }, 
51660             {
51661                 cls : '  x-signature-btn x-signature-'+id,
51662                 scope: editor, // was editor...
51663                 handler: this.confirmHandler,
51664                 clickEvent:'mousedown',
51665                 text: this.labels.confirm
51666             }
51667         );
51668     
51669     },
51670     //public
51671     /**
51672      * when user is clicked confirm then show this image.....
51673      * 
51674      * @return {String} Image Data URI
51675      */
51676     getImageDataURI : function(){
51677         var svg = this.svgEl.dom.parentNode.innerHTML;
51678         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
51679         return src; 
51680     },
51681     /**
51682      * 
51683      * @return {Boolean} this.isConfirmed
51684      */
51685     getConfirmed : function(){
51686         return this.isConfirmed;
51687     },
51688     /**
51689      * 
51690      * @return {Number} this.width
51691      */
51692     getWidth : function(){
51693         return this.width;
51694     },
51695     /**
51696      * 
51697      * @return {Number} this.height
51698      */
51699     getHeight : function(){
51700         return this.height;
51701     },
51702     // private
51703     getSignature : function(){
51704         return this.signatureTmp;
51705     },
51706     // private
51707     reset : function(){
51708         this.signatureTmp = '';
51709         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51710         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
51711         this.isConfirmed = false;
51712         Roo.form.Signature.superclass.reset.call(this);
51713     },
51714     setSignature : function(s){
51715         this.signatureTmp = s;
51716         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51717         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51718         this.setValue(s);
51719         this.isConfirmed = false;
51720         Roo.form.Signature.superclass.reset.call(this);
51721     }, 
51722     test : function(){
51723 //        Roo.log(this.signPanel.dom.contentWindow.up())
51724     },
51725     //private
51726     setConfirmed : function(){
51727         
51728         
51729         
51730 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51731     },
51732     // private
51733     confirmHandler : function(){
51734         if(!this.getSignature()){
51735             return;
51736         }
51737         
51738         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51739         this.setValue(this.getSignature());
51740         this.isConfirmed = true;
51741         
51742         this.fireEvent('confirm', this);
51743     },
51744     // private
51745     // Subclasses should provide the validation implementation by overriding this
51746     validateValue : function(value){
51747         if(this.allowBlank){
51748             return true;
51749         }
51750         
51751         if(this.isConfirmed){
51752             return true;
51753         }
51754         return false;
51755     }
51756 });/*
51757  * Based on:
51758  * Ext JS Library 1.1.1
51759  * Copyright(c) 2006-2007, Ext JS, LLC.
51760  *
51761  * Originally Released Under LGPL - original licence link has changed is not relivant.
51762  *
51763  * Fork - LGPL
51764  * <script type="text/javascript">
51765  */
51766  
51767
51768 /**
51769  * @class Roo.form.ComboBox
51770  * @extends Roo.form.TriggerField
51771  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51772  * @constructor
51773  * Create a new ComboBox.
51774  * @param {Object} config Configuration options
51775  */
51776 Roo.form.Select = function(config){
51777     Roo.form.Select.superclass.constructor.call(this, config);
51778      
51779 };
51780
51781 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51782     /**
51783      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51784      */
51785     /**
51786      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51787      * rendering into an Roo.Editor, defaults to false)
51788      */
51789     /**
51790      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51791      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51792      */
51793     /**
51794      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51795      */
51796     /**
51797      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51798      * the dropdown list (defaults to undefined, with no header element)
51799      */
51800
51801      /**
51802      * @cfg {String/Roo.Template} tpl The template to use to render the output
51803      */
51804      
51805     // private
51806     defaultAutoCreate : {tag: "select"  },
51807     /**
51808      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51809      */
51810     listWidth: undefined,
51811     /**
51812      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51813      * mode = 'remote' or 'text' if mode = 'local')
51814      */
51815     displayField: undefined,
51816     /**
51817      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51818      * mode = 'remote' or 'value' if mode = 'local'). 
51819      * Note: use of a valueField requires the user make a selection
51820      * in order for a value to be mapped.
51821      */
51822     valueField: undefined,
51823     
51824     
51825     /**
51826      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51827      * field's data value (defaults to the underlying DOM element's name)
51828      */
51829     hiddenName: undefined,
51830     /**
51831      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51832      */
51833     listClass: '',
51834     /**
51835      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51836      */
51837     selectedClass: 'x-combo-selected',
51838     /**
51839      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51840      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51841      * which displays a downward arrow icon).
51842      */
51843     triggerClass : 'x-form-arrow-trigger',
51844     /**
51845      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51846      */
51847     shadow:'sides',
51848     /**
51849      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51850      * anchor positions (defaults to 'tl-bl')
51851      */
51852     listAlign: 'tl-bl?',
51853     /**
51854      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51855      */
51856     maxHeight: 300,
51857     /**
51858      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51859      * query specified by the allQuery config option (defaults to 'query')
51860      */
51861     triggerAction: 'query',
51862     /**
51863      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51864      * (defaults to 4, does not apply if editable = false)
51865      */
51866     minChars : 4,
51867     /**
51868      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51869      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51870      */
51871     typeAhead: false,
51872     /**
51873      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51874      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51875      */
51876     queryDelay: 500,
51877     /**
51878      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51879      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51880      */
51881     pageSize: 0,
51882     /**
51883      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51884      * when editable = true (defaults to false)
51885      */
51886     selectOnFocus:false,
51887     /**
51888      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51889      */
51890     queryParam: 'query',
51891     /**
51892      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51893      * when mode = 'remote' (defaults to 'Loading...')
51894      */
51895     loadingText: 'Loading...',
51896     /**
51897      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51898      */
51899     resizable: false,
51900     /**
51901      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51902      */
51903     handleHeight : 8,
51904     /**
51905      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51906      * traditional select (defaults to true)
51907      */
51908     editable: true,
51909     /**
51910      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51911      */
51912     allQuery: '',
51913     /**
51914      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51915      */
51916     mode: 'remote',
51917     /**
51918      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51919      * listWidth has a higher value)
51920      */
51921     minListWidth : 70,
51922     /**
51923      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51924      * allow the user to set arbitrary text into the field (defaults to false)
51925      */
51926     forceSelection:false,
51927     /**
51928      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51929      * if typeAhead = true (defaults to 250)
51930      */
51931     typeAheadDelay : 250,
51932     /**
51933      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51934      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51935      */
51936     valueNotFoundText : undefined,
51937     
51938     /**
51939      * @cfg {String} defaultValue The value displayed after loading the store.
51940      */
51941     defaultValue: '',
51942     
51943     /**
51944      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51945      */
51946     blockFocus : false,
51947     
51948     /**
51949      * @cfg {Boolean} disableClear Disable showing of clear button.
51950      */
51951     disableClear : false,
51952     /**
51953      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51954      */
51955     alwaysQuery : false,
51956     
51957     //private
51958     addicon : false,
51959     editicon: false,
51960     
51961     // element that contains real text value.. (when hidden is used..)
51962      
51963     // private
51964     onRender : function(ct, position){
51965         Roo.form.Field.prototype.onRender.call(this, ct, position);
51966         
51967         if(this.store){
51968             this.store.on('beforeload', this.onBeforeLoad, this);
51969             this.store.on('load', this.onLoad, this);
51970             this.store.on('loadexception', this.onLoadException, this);
51971             this.store.load({});
51972         }
51973         
51974         
51975         
51976     },
51977
51978     // private
51979     initEvents : function(){
51980         //Roo.form.ComboBox.superclass.initEvents.call(this);
51981  
51982     },
51983
51984     onDestroy : function(){
51985        
51986         if(this.store){
51987             this.store.un('beforeload', this.onBeforeLoad, this);
51988             this.store.un('load', this.onLoad, this);
51989             this.store.un('loadexception', this.onLoadException, this);
51990         }
51991         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51992     },
51993
51994     // private
51995     fireKey : function(e){
51996         if(e.isNavKeyPress() && !this.list.isVisible()){
51997             this.fireEvent("specialkey", this, e);
51998         }
51999     },
52000
52001     // private
52002     onResize: function(w, h){
52003         
52004         return; 
52005     
52006         
52007     },
52008
52009     /**
52010      * Allow or prevent the user from directly editing the field text.  If false is passed,
52011      * the user will only be able to select from the items defined in the dropdown list.  This method
52012      * is the runtime equivalent of setting the 'editable' config option at config time.
52013      * @param {Boolean} value True to allow the user to directly edit the field text
52014      */
52015     setEditable : function(value){
52016          
52017     },
52018
52019     // private
52020     onBeforeLoad : function(){
52021         
52022         Roo.log("Select before load");
52023         return;
52024     
52025         this.innerList.update(this.loadingText ?
52026                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52027         //this.restrictHeight();
52028         this.selectedIndex = -1;
52029     },
52030
52031     // private
52032     onLoad : function(){
52033
52034     
52035         var dom = this.el.dom;
52036         dom.innerHTML = '';
52037          var od = dom.ownerDocument;
52038          
52039         if (this.emptyText) {
52040             var op = od.createElement('option');
52041             op.setAttribute('value', '');
52042             op.innerHTML = String.format('{0}', this.emptyText);
52043             dom.appendChild(op);
52044         }
52045         if(this.store.getCount() > 0){
52046            
52047             var vf = this.valueField;
52048             var df = this.displayField;
52049             this.store.data.each(function(r) {
52050                 // which colmsn to use... testing - cdoe / title..
52051                 var op = od.createElement('option');
52052                 op.setAttribute('value', r.data[vf]);
52053                 op.innerHTML = String.format('{0}', r.data[df]);
52054                 dom.appendChild(op);
52055             });
52056             if (typeof(this.defaultValue != 'undefined')) {
52057                 this.setValue(this.defaultValue);
52058             }
52059             
52060              
52061         }else{
52062             //this.onEmptyResults();
52063         }
52064         //this.el.focus();
52065     },
52066     // private
52067     onLoadException : function()
52068     {
52069         dom.innerHTML = '';
52070             
52071         Roo.log("Select on load exception");
52072         return;
52073     
52074         this.collapse();
52075         Roo.log(this.store.reader.jsonData);
52076         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52077             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52078         }
52079         
52080         
52081     },
52082     // private
52083     onTypeAhead : function(){
52084          
52085     },
52086
52087     // private
52088     onSelect : function(record, index){
52089         Roo.log('on select?');
52090         return;
52091         if(this.fireEvent('beforeselect', this, record, index) !== false){
52092             this.setFromData(index > -1 ? record.data : false);
52093             this.collapse();
52094             this.fireEvent('select', this, record, index);
52095         }
52096     },
52097
52098     /**
52099      * Returns the currently selected field value or empty string if no value is set.
52100      * @return {String} value The selected value
52101      */
52102     getValue : function(){
52103         var dom = this.el.dom;
52104         this.value = dom.options[dom.selectedIndex].value;
52105         return this.value;
52106         
52107     },
52108
52109     /**
52110      * Clears any text/value currently set in the field
52111      */
52112     clearValue : function(){
52113         this.value = '';
52114         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52115         
52116     },
52117
52118     /**
52119      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52120      * will be displayed in the field.  If the value does not match the data value of an existing item,
52121      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52122      * Otherwise the field will be blank (although the value will still be set).
52123      * @param {String} value The value to match
52124      */
52125     setValue : function(v){
52126         var d = this.el.dom;
52127         for (var i =0; i < d.options.length;i++) {
52128             if (v == d.options[i].value) {
52129                 d.selectedIndex = i;
52130                 this.value = v;
52131                 return;
52132             }
52133         }
52134         this.clearValue();
52135     },
52136     /**
52137      * @property {Object} the last set data for the element
52138      */
52139     
52140     lastData : false,
52141     /**
52142      * Sets the value of the field based on a object which is related to the record format for the store.
52143      * @param {Object} value the value to set as. or false on reset?
52144      */
52145     setFromData : function(o){
52146         Roo.log('setfrom data?');
52147          
52148         
52149         
52150     },
52151     // private
52152     reset : function(){
52153         this.clearValue();
52154     },
52155     // private
52156     findRecord : function(prop, value){
52157         
52158         return false;
52159     
52160         var record;
52161         if(this.store.getCount() > 0){
52162             this.store.each(function(r){
52163                 if(r.data[prop] == value){
52164                     record = r;
52165                     return false;
52166                 }
52167                 return true;
52168             });
52169         }
52170         return record;
52171     },
52172     
52173     getName: function()
52174     {
52175         // returns hidden if it's set..
52176         if (!this.rendered) {return ''};
52177         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52178         
52179     },
52180      
52181
52182     
52183
52184     // private
52185     onEmptyResults : function(){
52186         Roo.log('empty results');
52187         //this.collapse();
52188     },
52189
52190     /**
52191      * Returns true if the dropdown list is expanded, else false.
52192      */
52193     isExpanded : function(){
52194         return false;
52195     },
52196
52197     /**
52198      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52199      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52200      * @param {String} value The data value of the item to select
52201      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52202      * selected item if it is not currently in view (defaults to true)
52203      * @return {Boolean} True if the value matched an item in the list, else false
52204      */
52205     selectByValue : function(v, scrollIntoView){
52206         Roo.log('select By Value');
52207         return false;
52208     
52209         if(v !== undefined && v !== null){
52210             var r = this.findRecord(this.valueField || this.displayField, v);
52211             if(r){
52212                 this.select(this.store.indexOf(r), scrollIntoView);
52213                 return true;
52214             }
52215         }
52216         return false;
52217     },
52218
52219     /**
52220      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52221      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52222      * @param {Number} index The zero-based index of the list item to select
52223      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52224      * selected item if it is not currently in view (defaults to true)
52225      */
52226     select : function(index, scrollIntoView){
52227         Roo.log('select ');
52228         return  ;
52229         
52230         this.selectedIndex = index;
52231         this.view.select(index);
52232         if(scrollIntoView !== false){
52233             var el = this.view.getNode(index);
52234             if(el){
52235                 this.innerList.scrollChildIntoView(el, false);
52236             }
52237         }
52238     },
52239
52240       
52241
52242     // private
52243     validateBlur : function(){
52244         
52245         return;
52246         
52247     },
52248
52249     // private
52250     initQuery : function(){
52251         this.doQuery(this.getRawValue());
52252     },
52253
52254     // private
52255     doForce : function(){
52256         if(this.el.dom.value.length > 0){
52257             this.el.dom.value =
52258                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52259              
52260         }
52261     },
52262
52263     /**
52264      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52265      * query allowing the query action to be canceled if needed.
52266      * @param {String} query The SQL query to execute
52267      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52268      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52269      * saved in the current store (defaults to false)
52270      */
52271     doQuery : function(q, forceAll){
52272         
52273         Roo.log('doQuery?');
52274         if(q === undefined || q === null){
52275             q = '';
52276         }
52277         var qe = {
52278             query: q,
52279             forceAll: forceAll,
52280             combo: this,
52281             cancel:false
52282         };
52283         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52284             return false;
52285         }
52286         q = qe.query;
52287         forceAll = qe.forceAll;
52288         if(forceAll === true || (q.length >= this.minChars)){
52289             if(this.lastQuery != q || this.alwaysQuery){
52290                 this.lastQuery = q;
52291                 if(this.mode == 'local'){
52292                     this.selectedIndex = -1;
52293                     if(forceAll){
52294                         this.store.clearFilter();
52295                     }else{
52296                         this.store.filter(this.displayField, q);
52297                     }
52298                     this.onLoad();
52299                 }else{
52300                     this.store.baseParams[this.queryParam] = q;
52301                     this.store.load({
52302                         params: this.getParams(q)
52303                     });
52304                     this.expand();
52305                 }
52306             }else{
52307                 this.selectedIndex = -1;
52308                 this.onLoad();   
52309             }
52310         }
52311     },
52312
52313     // private
52314     getParams : function(q){
52315         var p = {};
52316         //p[this.queryParam] = q;
52317         if(this.pageSize){
52318             p.start = 0;
52319             p.limit = this.pageSize;
52320         }
52321         return p;
52322     },
52323
52324     /**
52325      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52326      */
52327     collapse : function(){
52328         
52329     },
52330
52331     // private
52332     collapseIf : function(e){
52333         
52334     },
52335
52336     /**
52337      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52338      */
52339     expand : function(){
52340         
52341     } ,
52342
52343     // private
52344      
52345
52346     /** 
52347     * @cfg {Boolean} grow 
52348     * @hide 
52349     */
52350     /** 
52351     * @cfg {Number} growMin 
52352     * @hide 
52353     */
52354     /** 
52355     * @cfg {Number} growMax 
52356     * @hide 
52357     */
52358     /**
52359      * @hide
52360      * @method autoSize
52361      */
52362     
52363     setWidth : function()
52364     {
52365         
52366     },
52367     getResizeEl : function(){
52368         return this.el;
52369     }
52370 });//<script type="text/javasscript">
52371  
52372
52373 /**
52374  * @class Roo.DDView
52375  * A DnD enabled version of Roo.View.
52376  * @param {Element/String} container The Element in which to create the View.
52377  * @param {String} tpl The template string used to create the markup for each element of the View
52378  * @param {Object} config The configuration properties. These include all the config options of
52379  * {@link Roo.View} plus some specific to this class.<br>
52380  * <p>
52381  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52382  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52383  * <p>
52384  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52385 .x-view-drag-insert-above {
52386         border-top:1px dotted #3366cc;
52387 }
52388 .x-view-drag-insert-below {
52389         border-bottom:1px dotted #3366cc;
52390 }
52391 </code></pre>
52392  * 
52393  */
52394  
52395 Roo.DDView = function(container, tpl, config) {
52396     Roo.DDView.superclass.constructor.apply(this, arguments);
52397     this.getEl().setStyle("outline", "0px none");
52398     this.getEl().unselectable();
52399     if (this.dragGroup) {
52400         this.setDraggable(this.dragGroup.split(","));
52401     }
52402     if (this.dropGroup) {
52403         this.setDroppable(this.dropGroup.split(","));
52404     }
52405     if (this.deletable) {
52406         this.setDeletable();
52407     }
52408     this.isDirtyFlag = false;
52409         this.addEvents({
52410                 "drop" : true
52411         });
52412 };
52413
52414 Roo.extend(Roo.DDView, Roo.View, {
52415 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52416 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52417 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52418 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52419
52420         isFormField: true,
52421
52422         reset: Roo.emptyFn,
52423         
52424         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52425
52426         validate: function() {
52427                 return true;
52428         },
52429         
52430         destroy: function() {
52431                 this.purgeListeners();
52432                 this.getEl.removeAllListeners();
52433                 this.getEl().remove();
52434                 if (this.dragZone) {
52435                         if (this.dragZone.destroy) {
52436                                 this.dragZone.destroy();
52437                         }
52438                 }
52439                 if (this.dropZone) {
52440                         if (this.dropZone.destroy) {
52441                                 this.dropZone.destroy();
52442                         }
52443                 }
52444         },
52445
52446 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
52447         getName: function() {
52448                 return this.name;
52449         },
52450
52451 /**     Loads the View from a JSON string representing the Records to put into the Store. */
52452         setValue: function(v) {
52453                 if (!this.store) {
52454                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
52455                 }
52456                 var data = {};
52457                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
52458                 this.store.proxy = new Roo.data.MemoryProxy(data);
52459                 this.store.load();
52460         },
52461
52462 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
52463         getValue: function() {
52464                 var result = '(';
52465                 this.store.each(function(rec) {
52466                         result += rec.id + ',';
52467                 });
52468                 return result.substr(0, result.length - 1) + ')';
52469         },
52470         
52471         getIds: function() {
52472                 var i = 0, result = new Array(this.store.getCount());
52473                 this.store.each(function(rec) {
52474                         result[i++] = rec.id;
52475                 });
52476                 return result;
52477         },
52478         
52479         isDirty: function() {
52480                 return this.isDirtyFlag;
52481         },
52482
52483 /**
52484  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
52485  *      whole Element becomes the target, and this causes the drop gesture to append.
52486  */
52487     getTargetFromEvent : function(e) {
52488                 var target = e.getTarget();
52489                 while ((target !== null) && (target.parentNode != this.el.dom)) {
52490                 target = target.parentNode;
52491                 }
52492                 if (!target) {
52493                         target = this.el.dom.lastChild || this.el.dom;
52494                 }
52495                 return target;
52496     },
52497
52498 /**
52499  *      Create the drag data which consists of an object which has the property "ddel" as
52500  *      the drag proxy element. 
52501  */
52502     getDragData : function(e) {
52503         var target = this.findItemFromChild(e.getTarget());
52504                 if(target) {
52505                         this.handleSelection(e);
52506                         var selNodes = this.getSelectedNodes();
52507             var dragData = {
52508                 source: this,
52509                 copy: this.copy || (this.allowCopy && e.ctrlKey),
52510                 nodes: selNodes,
52511                 records: []
52512                         };
52513                         var selectedIndices = this.getSelectedIndexes();
52514                         for (var i = 0; i < selectedIndices.length; i++) {
52515                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
52516                         }
52517                         if (selNodes.length == 1) {
52518                                 dragData.ddel = target.cloneNode(true); // the div element
52519                         } else {
52520                                 var div = document.createElement('div'); // create the multi element drag "ghost"
52521                                 div.className = 'multi-proxy';
52522                                 for (var i = 0, len = selNodes.length; i < len; i++) {
52523                                         div.appendChild(selNodes[i].cloneNode(true));
52524                                 }
52525                                 dragData.ddel = div;
52526                         }
52527             //console.log(dragData)
52528             //console.log(dragData.ddel.innerHTML)
52529                         return dragData;
52530                 }
52531         //console.log('nodragData')
52532                 return false;
52533     },
52534     
52535 /**     Specify to which ddGroup items in this DDView may be dragged. */
52536     setDraggable: function(ddGroup) {
52537         if (ddGroup instanceof Array) {
52538                 Roo.each(ddGroup, this.setDraggable, this);
52539                 return;
52540         }
52541         if (this.dragZone) {
52542                 this.dragZone.addToGroup(ddGroup);
52543         } else {
52544                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
52545                                 containerScroll: true,
52546                                 ddGroup: ddGroup 
52547
52548                         });
52549 //                      Draggability implies selection. DragZone's mousedown selects the element.
52550                         if (!this.multiSelect) { this.singleSelect = true; }
52551
52552 //                      Wire the DragZone's handlers up to methods in *this*
52553                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
52554                 }
52555     },
52556
52557 /**     Specify from which ddGroup this DDView accepts drops. */
52558     setDroppable: function(ddGroup) {
52559         if (ddGroup instanceof Array) {
52560                 Roo.each(ddGroup, this.setDroppable, this);
52561                 return;
52562         }
52563         if (this.dropZone) {
52564                 this.dropZone.addToGroup(ddGroup);
52565         } else {
52566                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
52567                                 containerScroll: true,
52568                                 ddGroup: ddGroup
52569                         });
52570
52571 //                      Wire the DropZone's handlers up to methods in *this*
52572                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
52573                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
52574                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
52575                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
52576                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
52577                 }
52578     },
52579
52580 /**     Decide whether to drop above or below a View node. */
52581     getDropPoint : function(e, n, dd){
52582         if (n == this.el.dom) { return "above"; }
52583                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
52584                 var c = t + (b - t) / 2;
52585                 var y = Roo.lib.Event.getPageY(e);
52586                 if(y <= c) {
52587                         return "above";
52588                 }else{
52589                         return "below";
52590                 }
52591     },
52592
52593     onNodeEnter : function(n, dd, e, data){
52594                 return false;
52595     },
52596     
52597     onNodeOver : function(n, dd, e, data){
52598                 var pt = this.getDropPoint(e, n, dd);
52599                 // set the insert point style on the target node
52600                 var dragElClass = this.dropNotAllowed;
52601                 if (pt) {
52602                         var targetElClass;
52603                         if (pt == "above"){
52604                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
52605                                 targetElClass = "x-view-drag-insert-above";
52606                         } else {
52607                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
52608                                 targetElClass = "x-view-drag-insert-below";
52609                         }
52610                         if (this.lastInsertClass != targetElClass){
52611                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
52612                                 this.lastInsertClass = targetElClass;
52613                         }
52614                 }
52615                 return dragElClass;
52616         },
52617
52618     onNodeOut : function(n, dd, e, data){
52619                 this.removeDropIndicators(n);
52620     },
52621
52622     onNodeDrop : function(n, dd, e, data){
52623         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
52624                 return false;
52625         }
52626         var pt = this.getDropPoint(e, n, dd);
52627                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
52628                 if (pt == "below") { insertAt++; }
52629                 for (var i = 0; i < data.records.length; i++) {
52630                         var r = data.records[i];
52631                         var dup = this.store.getById(r.id);
52632                         if (dup && (dd != this.dragZone)) {
52633                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
52634                         } else {
52635                                 if (data.copy) {
52636                                         this.store.insert(insertAt++, r.copy());
52637                                 } else {
52638                                         data.source.isDirtyFlag = true;
52639                                         r.store.remove(r);
52640                                         this.store.insert(insertAt++, r);
52641                                 }
52642                                 this.isDirtyFlag = true;
52643                         }
52644                 }
52645                 this.dragZone.cachedTarget = null;
52646                 return true;
52647     },
52648
52649     removeDropIndicators : function(n){
52650                 if(n){
52651                         Roo.fly(n).removeClass([
52652                                 "x-view-drag-insert-above",
52653                                 "x-view-drag-insert-below"]);
52654                         this.lastInsertClass = "_noclass";
52655                 }
52656     },
52657
52658 /**
52659  *      Utility method. Add a delete option to the DDView's context menu.
52660  *      @param {String} imageUrl The URL of the "delete" icon image.
52661  */
52662         setDeletable: function(imageUrl) {
52663                 if (!this.singleSelect && !this.multiSelect) {
52664                         this.singleSelect = true;
52665                 }
52666                 var c = this.getContextMenu();
52667                 this.contextMenu.on("itemclick", function(item) {
52668                         switch (item.id) {
52669                                 case "delete":
52670                                         this.remove(this.getSelectedIndexes());
52671                                         break;
52672                         }
52673                 }, this);
52674                 this.contextMenu.add({
52675                         icon: imageUrl,
52676                         id: "delete",
52677                         text: 'Delete'
52678                 });
52679         },
52680         
52681 /**     Return the context menu for this DDView. */
52682         getContextMenu: function() {
52683                 if (!this.contextMenu) {
52684 //                      Create the View's context menu
52685                         this.contextMenu = new Roo.menu.Menu({
52686                                 id: this.id + "-contextmenu"
52687                         });
52688                         this.el.on("contextmenu", this.showContextMenu, this);
52689                 }
52690                 return this.contextMenu;
52691         },
52692         
52693         disableContextMenu: function() {
52694                 if (this.contextMenu) {
52695                         this.el.un("contextmenu", this.showContextMenu, this);
52696                 }
52697         },
52698
52699         showContextMenu: function(e, item) {
52700         item = this.findItemFromChild(e.getTarget());
52701                 if (item) {
52702                         e.stopEvent();
52703                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
52704                         this.contextMenu.showAt(e.getXY());
52705             }
52706     },
52707
52708 /**
52709  *      Remove {@link Roo.data.Record}s at the specified indices.
52710  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
52711  */
52712     remove: function(selectedIndices) {
52713                 selectedIndices = [].concat(selectedIndices);
52714                 for (var i = 0; i < selectedIndices.length; i++) {
52715                         var rec = this.store.getAt(selectedIndices[i]);
52716                         this.store.remove(rec);
52717                 }
52718     },
52719
52720 /**
52721  *      Double click fires the event, but also, if this is draggable, and there is only one other
52722  *      related DropZone, it transfers the selected node.
52723  */
52724     onDblClick : function(e){
52725         var item = this.findItemFromChild(e.getTarget());
52726         if(item){
52727             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52728                 return false;
52729             }
52730             if (this.dragGroup) {
52731                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52732                     while (targets.indexOf(this.dropZone) > -1) {
52733                             targets.remove(this.dropZone);
52734                                 }
52735                     if (targets.length == 1) {
52736                                         this.dragZone.cachedTarget = null;
52737                         var el = Roo.get(targets[0].getEl());
52738                         var box = el.getBox(true);
52739                         targets[0].onNodeDrop(el.dom, {
52740                                 target: el.dom,
52741                                 xy: [box.x, box.y + box.height - 1]
52742                         }, null, this.getDragData(e));
52743                     }
52744                 }
52745         }
52746     },
52747     
52748     handleSelection: function(e) {
52749                 this.dragZone.cachedTarget = null;
52750         var item = this.findItemFromChild(e.getTarget());
52751         if (!item) {
52752                 this.clearSelections(true);
52753                 return;
52754         }
52755                 if (item && (this.multiSelect || this.singleSelect)){
52756                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52757                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52758                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52759                                 this.unselect(item);
52760                         } else {
52761                                 this.select(item, this.multiSelect && e.ctrlKey);
52762                                 this.lastSelection = item;
52763                         }
52764                 }
52765     },
52766
52767     onItemClick : function(item, index, e){
52768                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52769                         return false;
52770                 }
52771                 return true;
52772     },
52773
52774     unselect : function(nodeInfo, suppressEvent){
52775                 var node = this.getNode(nodeInfo);
52776                 if(node && this.isSelected(node)){
52777                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52778                                 Roo.fly(node).removeClass(this.selectedClass);
52779                                 this.selections.remove(node);
52780                                 if(!suppressEvent){
52781                                         this.fireEvent("selectionchange", this, this.selections);
52782                                 }
52783                         }
52784                 }
52785     }
52786 });
52787 /*
52788  * Based on:
52789  * Ext JS Library 1.1.1
52790  * Copyright(c) 2006-2007, Ext JS, LLC.
52791  *
52792  * Originally Released Under LGPL - original licence link has changed is not relivant.
52793  *
52794  * Fork - LGPL
52795  * <script type="text/javascript">
52796  */
52797  
52798 /**
52799  * @class Roo.LayoutManager
52800  * @extends Roo.util.Observable
52801  * Base class for layout managers.
52802  */
52803 Roo.LayoutManager = function(container, config){
52804     Roo.LayoutManager.superclass.constructor.call(this);
52805     this.el = Roo.get(container);
52806     // ie scrollbar fix
52807     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52808         document.body.scroll = "no";
52809     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52810         this.el.position('relative');
52811     }
52812     this.id = this.el.id;
52813     this.el.addClass("x-layout-container");
52814     /** false to disable window resize monitoring @type Boolean */
52815     this.monitorWindowResize = true;
52816     this.regions = {};
52817     this.addEvents({
52818         /**
52819          * @event layout
52820          * Fires when a layout is performed. 
52821          * @param {Roo.LayoutManager} this
52822          */
52823         "layout" : true,
52824         /**
52825          * @event regionresized
52826          * Fires when the user resizes a region. 
52827          * @param {Roo.LayoutRegion} region The resized region
52828          * @param {Number} newSize The new size (width for east/west, height for north/south)
52829          */
52830         "regionresized" : true,
52831         /**
52832          * @event regioncollapsed
52833          * Fires when a region is collapsed. 
52834          * @param {Roo.LayoutRegion} region The collapsed region
52835          */
52836         "regioncollapsed" : true,
52837         /**
52838          * @event regionexpanded
52839          * Fires when a region is expanded.  
52840          * @param {Roo.LayoutRegion} region The expanded region
52841          */
52842         "regionexpanded" : true
52843     });
52844     this.updating = false;
52845     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52846 };
52847
52848 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52849     /**
52850      * Returns true if this layout is currently being updated
52851      * @return {Boolean}
52852      */
52853     isUpdating : function(){
52854         return this.updating; 
52855     },
52856     
52857     /**
52858      * Suspend the LayoutManager from doing auto-layouts while
52859      * making multiple add or remove calls
52860      */
52861     beginUpdate : function(){
52862         this.updating = true;    
52863     },
52864     
52865     /**
52866      * Restore auto-layouts and optionally disable the manager from performing a layout
52867      * @param {Boolean} noLayout true to disable a layout update 
52868      */
52869     endUpdate : function(noLayout){
52870         this.updating = false;
52871         if(!noLayout){
52872             this.layout();
52873         }    
52874     },
52875     
52876     layout: function(){
52877         
52878     },
52879     
52880     onRegionResized : function(region, newSize){
52881         this.fireEvent("regionresized", region, newSize);
52882         this.layout();
52883     },
52884     
52885     onRegionCollapsed : function(region){
52886         this.fireEvent("regioncollapsed", region);
52887     },
52888     
52889     onRegionExpanded : function(region){
52890         this.fireEvent("regionexpanded", region);
52891     },
52892         
52893     /**
52894      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52895      * performs box-model adjustments.
52896      * @return {Object} The size as an object {width: (the width), height: (the height)}
52897      */
52898     getViewSize : function(){
52899         var size;
52900         if(this.el.dom != document.body){
52901             size = this.el.getSize();
52902         }else{
52903             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52904         }
52905         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52906         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52907         return size;
52908     },
52909     
52910     /**
52911      * Returns the Element this layout is bound to.
52912      * @return {Roo.Element}
52913      */
52914     getEl : function(){
52915         return this.el;
52916     },
52917     
52918     /**
52919      * Returns the specified region.
52920      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52921      * @return {Roo.LayoutRegion}
52922      */
52923     getRegion : function(target){
52924         return this.regions[target.toLowerCase()];
52925     },
52926     
52927     onWindowResize : function(){
52928         if(this.monitorWindowResize){
52929             this.layout();
52930         }
52931     }
52932 });/*
52933  * Based on:
52934  * Ext JS Library 1.1.1
52935  * Copyright(c) 2006-2007, Ext JS, LLC.
52936  *
52937  * Originally Released Under LGPL - original licence link has changed is not relivant.
52938  *
52939  * Fork - LGPL
52940  * <script type="text/javascript">
52941  */
52942 /**
52943  * @class Roo.BorderLayout
52944  * @extends Roo.LayoutManager
52945  * @children Roo.ContentPanel
52946  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52947  * please see: <br><br>
52948  * <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>
52949  * <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>
52950  * Example:
52951  <pre><code>
52952  var layout = new Roo.BorderLayout(document.body, {
52953     north: {
52954         initialSize: 25,
52955         titlebar: false
52956     },
52957     west: {
52958         split:true,
52959         initialSize: 200,
52960         minSize: 175,
52961         maxSize: 400,
52962         titlebar: true,
52963         collapsible: true
52964     },
52965     east: {
52966         split:true,
52967         initialSize: 202,
52968         minSize: 175,
52969         maxSize: 400,
52970         titlebar: true,
52971         collapsible: true
52972     },
52973     south: {
52974         split:true,
52975         initialSize: 100,
52976         minSize: 100,
52977         maxSize: 200,
52978         titlebar: true,
52979         collapsible: true
52980     },
52981     center: {
52982         titlebar: true,
52983         autoScroll:true,
52984         resizeTabs: true,
52985         minTabWidth: 50,
52986         preferredTabWidth: 150
52987     }
52988 });
52989
52990 // shorthand
52991 var CP = Roo.ContentPanel;
52992
52993 layout.beginUpdate();
52994 layout.add("north", new CP("north", "North"));
52995 layout.add("south", new CP("south", {title: "South", closable: true}));
52996 layout.add("west", new CP("west", {title: "West"}));
52997 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52998 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52999 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53000 layout.getRegion("center").showPanel("center1");
53001 layout.endUpdate();
53002 </code></pre>
53003
53004 <b>The container the layout is rendered into can be either the body element or any other element.
53005 If it is not the body element, the container needs to either be an absolute positioned element,
53006 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53007 the container size if it is not the body element.</b>
53008
53009 * @constructor
53010 * Create a new BorderLayout
53011 * @param {String/HTMLElement/Element} container The container this layout is bound to
53012 * @param {Object} config Configuration options
53013  */
53014 Roo.BorderLayout = function(container, config){
53015     config = config || {};
53016     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53017     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53018     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53019         var target = this.factory.validRegions[i];
53020         if(config[target]){
53021             this.addRegion(target, config[target]);
53022         }
53023     }
53024 };
53025
53026 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53027         
53028         /**
53029          * @cfg {Roo.LayoutRegion} east
53030          */
53031         /**
53032          * @cfg {Roo.LayoutRegion} west
53033          */
53034         /**
53035          * @cfg {Roo.LayoutRegion} north
53036          */
53037         /**
53038          * @cfg {Roo.LayoutRegion} south
53039          */
53040         /**
53041          * @cfg {Roo.LayoutRegion} center
53042          */
53043     /**
53044      * Creates and adds a new region if it doesn't already exist.
53045      * @param {String} target The target region key (north, south, east, west or center).
53046      * @param {Object} config The regions config object
53047      * @return {BorderLayoutRegion} The new region
53048      */
53049     addRegion : function(target, config){
53050         if(!this.regions[target]){
53051             var r = this.factory.create(target, this, config);
53052             this.bindRegion(target, r);
53053         }
53054         return this.regions[target];
53055     },
53056
53057     // private (kinda)
53058     bindRegion : function(name, r){
53059         this.regions[name] = r;
53060         r.on("visibilitychange", this.layout, this);
53061         r.on("paneladded", this.layout, this);
53062         r.on("panelremoved", this.layout, this);
53063         r.on("invalidated", this.layout, this);
53064         r.on("resized", this.onRegionResized, this);
53065         r.on("collapsed", this.onRegionCollapsed, this);
53066         r.on("expanded", this.onRegionExpanded, this);
53067     },
53068
53069     /**
53070      * Performs a layout update.
53071      */
53072     layout : function(){
53073         if(this.updating) {
53074             return;
53075         }
53076         var size = this.getViewSize();
53077         var w = size.width;
53078         var h = size.height;
53079         var centerW = w;
53080         var centerH = h;
53081         var centerY = 0;
53082         var centerX = 0;
53083         //var x = 0, y = 0;
53084
53085         var rs = this.regions;
53086         var north = rs["north"];
53087         var south = rs["south"]; 
53088         var west = rs["west"];
53089         var east = rs["east"];
53090         var center = rs["center"];
53091         //if(this.hideOnLayout){ // not supported anymore
53092             //c.el.setStyle("display", "none");
53093         //}
53094         if(north && north.isVisible()){
53095             var b = north.getBox();
53096             var m = north.getMargins();
53097             b.width = w - (m.left+m.right);
53098             b.x = m.left;
53099             b.y = m.top;
53100             centerY = b.height + b.y + m.bottom;
53101             centerH -= centerY;
53102             north.updateBox(this.safeBox(b));
53103         }
53104         if(south && south.isVisible()){
53105             var b = south.getBox();
53106             var m = south.getMargins();
53107             b.width = w - (m.left+m.right);
53108             b.x = m.left;
53109             var totalHeight = (b.height + m.top + m.bottom);
53110             b.y = h - totalHeight + m.top;
53111             centerH -= totalHeight;
53112             south.updateBox(this.safeBox(b));
53113         }
53114         if(west && west.isVisible()){
53115             var b = west.getBox();
53116             var m = west.getMargins();
53117             b.height = centerH - (m.top+m.bottom);
53118             b.x = m.left;
53119             b.y = centerY + m.top;
53120             var totalWidth = (b.width + m.left + m.right);
53121             centerX += totalWidth;
53122             centerW -= totalWidth;
53123             west.updateBox(this.safeBox(b));
53124         }
53125         if(east && east.isVisible()){
53126             var b = east.getBox();
53127             var m = east.getMargins();
53128             b.height = centerH - (m.top+m.bottom);
53129             var totalWidth = (b.width + m.left + m.right);
53130             b.x = w - totalWidth + m.left;
53131             b.y = centerY + m.top;
53132             centerW -= totalWidth;
53133             east.updateBox(this.safeBox(b));
53134         }
53135         if(center){
53136             var m = center.getMargins();
53137             var centerBox = {
53138                 x: centerX + m.left,
53139                 y: centerY + m.top,
53140                 width: centerW - (m.left+m.right),
53141                 height: centerH - (m.top+m.bottom)
53142             };
53143             //if(this.hideOnLayout){
53144                 //center.el.setStyle("display", "block");
53145             //}
53146             center.updateBox(this.safeBox(centerBox));
53147         }
53148         this.el.repaint();
53149         this.fireEvent("layout", this);
53150     },
53151
53152     // private
53153     safeBox : function(box){
53154         box.width = Math.max(0, box.width);
53155         box.height = Math.max(0, box.height);
53156         return box;
53157     },
53158
53159     /**
53160      * Adds a ContentPanel (or subclass) to this layout.
53161      * @param {String} target The target region key (north, south, east, west or center).
53162      * @param {Roo.ContentPanel} panel The panel to add
53163      * @return {Roo.ContentPanel} The added panel
53164      */
53165     add : function(target, panel){
53166          
53167         target = target.toLowerCase();
53168         return this.regions[target].add(panel);
53169     },
53170
53171     /**
53172      * Remove a ContentPanel (or subclass) to this layout.
53173      * @param {String} target The target region key (north, south, east, west or center).
53174      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53175      * @return {Roo.ContentPanel} The removed panel
53176      */
53177     remove : function(target, panel){
53178         target = target.toLowerCase();
53179         return this.regions[target].remove(panel);
53180     },
53181
53182     /**
53183      * Searches all regions for a panel with the specified id
53184      * @param {String} panelId
53185      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53186      */
53187     findPanel : function(panelId){
53188         var rs = this.regions;
53189         for(var target in rs){
53190             if(typeof rs[target] != "function"){
53191                 var p = rs[target].getPanel(panelId);
53192                 if(p){
53193                     return p;
53194                 }
53195             }
53196         }
53197         return null;
53198     },
53199
53200     /**
53201      * Searches all regions for a panel with the specified id and activates (shows) it.
53202      * @param {String/ContentPanel} panelId The panels id or the panel itself
53203      * @return {Roo.ContentPanel} The shown panel or null
53204      */
53205     showPanel : function(panelId) {
53206       var rs = this.regions;
53207       for(var target in rs){
53208          var r = rs[target];
53209          if(typeof r != "function"){
53210             if(r.hasPanel(panelId)){
53211                return r.showPanel(panelId);
53212             }
53213          }
53214       }
53215       return null;
53216    },
53217
53218    /**
53219      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53220      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53221      */
53222     restoreState : function(provider){
53223         if(!provider){
53224             provider = Roo.state.Manager;
53225         }
53226         var sm = new Roo.LayoutStateManager();
53227         sm.init(this, provider);
53228     },
53229
53230     /**
53231      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53232      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53233      * a valid ContentPanel config object.  Example:
53234      * <pre><code>
53235 // Create the main layout
53236 var layout = new Roo.BorderLayout('main-ct', {
53237     west: {
53238         split:true,
53239         minSize: 175,
53240         titlebar: true
53241     },
53242     center: {
53243         title:'Components'
53244     }
53245 }, 'main-ct');
53246
53247 // Create and add multiple ContentPanels at once via configs
53248 layout.batchAdd({
53249    west: {
53250        id: 'source-files',
53251        autoCreate:true,
53252        title:'Ext Source Files',
53253        autoScroll:true,
53254        fitToFrame:true
53255    },
53256    center : {
53257        el: cview,
53258        autoScroll:true,
53259        fitToFrame:true,
53260        toolbar: tb,
53261        resizeEl:'cbody'
53262    }
53263 });
53264 </code></pre>
53265      * @param {Object} regions An object containing ContentPanel configs by region name
53266      */
53267     batchAdd : function(regions){
53268         this.beginUpdate();
53269         for(var rname in regions){
53270             var lr = this.regions[rname];
53271             if(lr){
53272                 this.addTypedPanels(lr, regions[rname]);
53273             }
53274         }
53275         this.endUpdate();
53276     },
53277
53278     // private
53279     addTypedPanels : function(lr, ps){
53280         if(typeof ps == 'string'){
53281             lr.add(new Roo.ContentPanel(ps));
53282         }
53283         else if(ps instanceof Array){
53284             for(var i =0, len = ps.length; i < len; i++){
53285                 this.addTypedPanels(lr, ps[i]);
53286             }
53287         }
53288         else if(!ps.events){ // raw config?
53289             var el = ps.el;
53290             delete ps.el; // prevent conflict
53291             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53292         }
53293         else {  // panel object assumed!
53294             lr.add(ps);
53295         }
53296     },
53297     /**
53298      * Adds a xtype elements to the layout.
53299      * <pre><code>
53300
53301 layout.addxtype({
53302        xtype : 'ContentPanel',
53303        region: 'west',
53304        items: [ .... ]
53305    }
53306 );
53307
53308 layout.addxtype({
53309         xtype : 'NestedLayoutPanel',
53310         region: 'west',
53311         layout: {
53312            center: { },
53313            west: { }   
53314         },
53315         items : [ ... list of content panels or nested layout panels.. ]
53316    }
53317 );
53318 </code></pre>
53319      * @param {Object} cfg Xtype definition of item to add.
53320      */
53321     addxtype : function(cfg)
53322     {
53323         // basically accepts a pannel...
53324         // can accept a layout region..!?!?
53325         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53326         
53327         if (!cfg.xtype.match(/Panel$/)) {
53328             return false;
53329         }
53330         var ret = false;
53331         
53332         if (typeof(cfg.region) == 'undefined') {
53333             Roo.log("Failed to add Panel, region was not set");
53334             Roo.log(cfg);
53335             return false;
53336         }
53337         var region = cfg.region;
53338         delete cfg.region;
53339         
53340           
53341         var xitems = [];
53342         if (cfg.items) {
53343             xitems = cfg.items;
53344             delete cfg.items;
53345         }
53346         var nb = false;
53347         
53348         switch(cfg.xtype) 
53349         {
53350             case 'ContentPanel':  // ContentPanel (el, cfg)
53351             case 'ScrollPanel':  // ContentPanel (el, cfg)
53352             case 'ViewPanel': 
53353                 if(cfg.autoCreate) {
53354                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53355                 } else {
53356                     var el = this.el.createChild();
53357                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53358                 }
53359                 
53360                 this.add(region, ret);
53361                 break;
53362             
53363             
53364             case 'TreePanel': // our new panel!
53365                 cfg.el = this.el.createChild();
53366                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53367                 this.add(region, ret);
53368                 break;
53369             
53370             case 'NestedLayoutPanel': 
53371                 // create a new Layout (which is  a Border Layout...
53372                 var el = this.el.createChild();
53373                 var clayout = cfg.layout;
53374                 delete cfg.layout;
53375                 clayout.items   = clayout.items  || [];
53376                 // replace this exitems with the clayout ones..
53377                 xitems = clayout.items;
53378                  
53379                 
53380                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53381                     cfg.background = false;
53382                 }
53383                 var layout = new Roo.BorderLayout(el, clayout);
53384                 
53385                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53386                 //console.log('adding nested layout panel '  + cfg.toSource());
53387                 this.add(region, ret);
53388                 nb = {}; /// find first...
53389                 break;
53390                 
53391             case 'GridPanel': 
53392             
53393                 // needs grid and region
53394                 
53395                 //var el = this.getRegion(region).el.createChild();
53396                 var el = this.el.createChild();
53397                 // create the grid first...
53398                 
53399                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53400                 delete cfg.grid;
53401                 if (region == 'center' && this.active ) {
53402                     cfg.background = false;
53403                 }
53404                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53405                 
53406                 this.add(region, ret);
53407                 if (cfg.background) {
53408                     ret.on('activate', function(gp) {
53409                         if (!gp.grid.rendered) {
53410                             gp.grid.render();
53411                         }
53412                     });
53413                 } else {
53414                     grid.render();
53415                 }
53416                 break;
53417            
53418            
53419            
53420                 
53421                 
53422                 
53423             default:
53424                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53425                     
53426                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53427                     this.add(region, ret);
53428                 } else {
53429                 
53430                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53431                     return null;
53432                 }
53433                 
53434              // GridPanel (grid, cfg)
53435             
53436         }
53437         this.beginUpdate();
53438         // add children..
53439         var region = '';
53440         var abn = {};
53441         Roo.each(xitems, function(i)  {
53442             region = nb && i.region ? i.region : false;
53443             
53444             var add = ret.addxtype(i);
53445            
53446             if (region) {
53447                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
53448                 if (!i.background) {
53449                     abn[region] = nb[region] ;
53450                 }
53451             }
53452             
53453         });
53454         this.endUpdate();
53455
53456         // make the last non-background panel active..
53457         //if (nb) { Roo.log(abn); }
53458         if (nb) {
53459             
53460             for(var r in abn) {
53461                 region = this.getRegion(r);
53462                 if (region) {
53463                     // tried using nb[r], but it does not work..
53464                      
53465                     region.showPanel(abn[r]);
53466                    
53467                 }
53468             }
53469         }
53470         return ret;
53471         
53472     }
53473 });
53474
53475 /**
53476  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
53477  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
53478  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
53479  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
53480  * <pre><code>
53481 // shorthand
53482 var CP = Roo.ContentPanel;
53483
53484 var layout = Roo.BorderLayout.create({
53485     north: {
53486         initialSize: 25,
53487         titlebar: false,
53488         panels: [new CP("north", "North")]
53489     },
53490     west: {
53491         split:true,
53492         initialSize: 200,
53493         minSize: 175,
53494         maxSize: 400,
53495         titlebar: true,
53496         collapsible: true,
53497         panels: [new CP("west", {title: "West"})]
53498     },
53499     east: {
53500         split:true,
53501         initialSize: 202,
53502         minSize: 175,
53503         maxSize: 400,
53504         titlebar: true,
53505         collapsible: true,
53506         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
53507     },
53508     south: {
53509         split:true,
53510         initialSize: 100,
53511         minSize: 100,
53512         maxSize: 200,
53513         titlebar: true,
53514         collapsible: true,
53515         panels: [new CP("south", {title: "South", closable: true})]
53516     },
53517     center: {
53518         titlebar: true,
53519         autoScroll:true,
53520         resizeTabs: true,
53521         minTabWidth: 50,
53522         preferredTabWidth: 150,
53523         panels: [
53524             new CP("center1", {title: "Close Me", closable: true}),
53525             new CP("center2", {title: "Center Panel", closable: false})
53526         ]
53527     }
53528 }, document.body);
53529
53530 layout.getRegion("center").showPanel("center1");
53531 </code></pre>
53532  * @param config
53533  * @param targetEl
53534  */
53535 Roo.BorderLayout.create = function(config, targetEl){
53536     var layout = new Roo.BorderLayout(targetEl || document.body, config);
53537     layout.beginUpdate();
53538     var regions = Roo.BorderLayout.RegionFactory.validRegions;
53539     for(var j = 0, jlen = regions.length; j < jlen; j++){
53540         var lr = regions[j];
53541         if(layout.regions[lr] && config[lr].panels){
53542             var r = layout.regions[lr];
53543             var ps = config[lr].panels;
53544             layout.addTypedPanels(r, ps);
53545         }
53546     }
53547     layout.endUpdate();
53548     return layout;
53549 };
53550
53551 // private
53552 Roo.BorderLayout.RegionFactory = {
53553     // private
53554     validRegions : ["north","south","east","west","center"],
53555
53556     // private
53557     create : function(target, mgr, config){
53558         target = target.toLowerCase();
53559         if(config.lightweight || config.basic){
53560             return new Roo.BasicLayoutRegion(mgr, config, target);
53561         }
53562         switch(target){
53563             case "north":
53564                 return new Roo.NorthLayoutRegion(mgr, config);
53565             case "south":
53566                 return new Roo.SouthLayoutRegion(mgr, config);
53567             case "east":
53568                 return new Roo.EastLayoutRegion(mgr, config);
53569             case "west":
53570                 return new Roo.WestLayoutRegion(mgr, config);
53571             case "center":
53572                 return new Roo.CenterLayoutRegion(mgr, config);
53573         }
53574         throw 'Layout region "'+target+'" not supported.';
53575     }
53576 };/*
53577  * Based on:
53578  * Ext JS Library 1.1.1
53579  * Copyright(c) 2006-2007, Ext JS, LLC.
53580  *
53581  * Originally Released Under LGPL - original licence link has changed is not relivant.
53582  *
53583  * Fork - LGPL
53584  * <script type="text/javascript">
53585  */
53586  
53587 /**
53588  * @class Roo.BasicLayoutRegion
53589  * @extends Roo.util.Observable
53590  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
53591  * and does not have a titlebar, tabs or any other features. All it does is size and position 
53592  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
53593  */
53594 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
53595     this.mgr = mgr;
53596     this.position  = pos;
53597     this.events = {
53598         /**
53599          * @scope Roo.BasicLayoutRegion
53600          */
53601         
53602         /**
53603          * @event beforeremove
53604          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
53605          * @param {Roo.LayoutRegion} this
53606          * @param {Roo.ContentPanel} panel The panel
53607          * @param {Object} e The cancel event object
53608          */
53609         "beforeremove" : true,
53610         /**
53611          * @event invalidated
53612          * Fires when the layout for this region is changed.
53613          * @param {Roo.LayoutRegion} this
53614          */
53615         "invalidated" : true,
53616         /**
53617          * @event visibilitychange
53618          * Fires when this region is shown or hidden 
53619          * @param {Roo.LayoutRegion} this
53620          * @param {Boolean} visibility true or false
53621          */
53622         "visibilitychange" : true,
53623         /**
53624          * @event paneladded
53625          * Fires when a panel is added. 
53626          * @param {Roo.LayoutRegion} this
53627          * @param {Roo.ContentPanel} panel The panel
53628          */
53629         "paneladded" : true,
53630         /**
53631          * @event panelremoved
53632          * Fires when a panel is removed. 
53633          * @param {Roo.LayoutRegion} this
53634          * @param {Roo.ContentPanel} panel The panel
53635          */
53636         "panelremoved" : true,
53637         /**
53638          * @event beforecollapse
53639          * Fires when this region before collapse.
53640          * @param {Roo.LayoutRegion} this
53641          */
53642         "beforecollapse" : true,
53643         /**
53644          * @event collapsed
53645          * Fires when this region is collapsed.
53646          * @param {Roo.LayoutRegion} this
53647          */
53648         "collapsed" : true,
53649         /**
53650          * @event expanded
53651          * Fires when this region is expanded.
53652          * @param {Roo.LayoutRegion} this
53653          */
53654         "expanded" : true,
53655         /**
53656          * @event slideshow
53657          * Fires when this region is slid into view.
53658          * @param {Roo.LayoutRegion} this
53659          */
53660         "slideshow" : true,
53661         /**
53662          * @event slidehide
53663          * Fires when this region slides out of view. 
53664          * @param {Roo.LayoutRegion} this
53665          */
53666         "slidehide" : true,
53667         /**
53668          * @event panelactivated
53669          * Fires when a panel is activated. 
53670          * @param {Roo.LayoutRegion} this
53671          * @param {Roo.ContentPanel} panel The activated panel
53672          */
53673         "panelactivated" : true,
53674         /**
53675          * @event resized
53676          * Fires when the user resizes this region. 
53677          * @param {Roo.LayoutRegion} this
53678          * @param {Number} newSize The new size (width for east/west, height for north/south)
53679          */
53680         "resized" : true
53681     };
53682     /** A collection of panels in this region. @type Roo.util.MixedCollection */
53683     this.panels = new Roo.util.MixedCollection();
53684     this.panels.getKey = this.getPanelId.createDelegate(this);
53685     this.box = null;
53686     this.activePanel = null;
53687     // ensure listeners are added...
53688     
53689     if (config.listeners || config.events) {
53690         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
53691             listeners : config.listeners || {},
53692             events : config.events || {}
53693         });
53694     }
53695     
53696     if(skipConfig !== true){
53697         this.applyConfig(config);
53698     }
53699 };
53700
53701 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
53702     getPanelId : function(p){
53703         return p.getId();
53704     },
53705     
53706     applyConfig : function(config){
53707         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53708         this.config = config;
53709         
53710     },
53711     
53712     /**
53713      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53714      * the width, for horizontal (north, south) the height.
53715      * @param {Number} newSize The new width or height
53716      */
53717     resizeTo : function(newSize){
53718         var el = this.el ? this.el :
53719                  (this.activePanel ? this.activePanel.getEl() : null);
53720         if(el){
53721             switch(this.position){
53722                 case "east":
53723                 case "west":
53724                     el.setWidth(newSize);
53725                     this.fireEvent("resized", this, newSize);
53726                 break;
53727                 case "north":
53728                 case "south":
53729                     el.setHeight(newSize);
53730                     this.fireEvent("resized", this, newSize);
53731                 break;                
53732             }
53733         }
53734     },
53735     
53736     getBox : function(){
53737         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53738     },
53739     
53740     getMargins : function(){
53741         return this.margins;
53742     },
53743     
53744     updateBox : function(box){
53745         this.box = box;
53746         var el = this.activePanel.getEl();
53747         el.dom.style.left = box.x + "px";
53748         el.dom.style.top = box.y + "px";
53749         this.activePanel.setSize(box.width, box.height);
53750     },
53751     
53752     /**
53753      * Returns the container element for this region.
53754      * @return {Roo.Element}
53755      */
53756     getEl : function(){
53757         return this.activePanel;
53758     },
53759     
53760     /**
53761      * Returns true if this region is currently visible.
53762      * @return {Boolean}
53763      */
53764     isVisible : function(){
53765         return this.activePanel ? true : false;
53766     },
53767     
53768     setActivePanel : function(panel){
53769         panel = this.getPanel(panel);
53770         if(this.activePanel && this.activePanel != panel){
53771             this.activePanel.setActiveState(false);
53772             this.activePanel.getEl().setLeftTop(-10000,-10000);
53773         }
53774         this.activePanel = panel;
53775         panel.setActiveState(true);
53776         if(this.box){
53777             panel.setSize(this.box.width, this.box.height);
53778         }
53779         this.fireEvent("panelactivated", this, panel);
53780         this.fireEvent("invalidated");
53781     },
53782     
53783     /**
53784      * Show the specified panel.
53785      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53786      * @return {Roo.ContentPanel} The shown panel or null
53787      */
53788     showPanel : function(panel){
53789         if(panel = this.getPanel(panel)){
53790             this.setActivePanel(panel);
53791         }
53792         return panel;
53793     },
53794     
53795     /**
53796      * Get the active panel for this region.
53797      * @return {Roo.ContentPanel} The active panel or null
53798      */
53799     getActivePanel : function(){
53800         return this.activePanel;
53801     },
53802     
53803     /**
53804      * Add the passed ContentPanel(s)
53805      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53806      * @return {Roo.ContentPanel} The panel added (if only one was added)
53807      */
53808     add : function(panel){
53809         if(arguments.length > 1){
53810             for(var i = 0, len = arguments.length; i < len; i++) {
53811                 this.add(arguments[i]);
53812             }
53813             return null;
53814         }
53815         if(this.hasPanel(panel)){
53816             this.showPanel(panel);
53817             return panel;
53818         }
53819         var el = panel.getEl();
53820         if(el.dom.parentNode != this.mgr.el.dom){
53821             this.mgr.el.dom.appendChild(el.dom);
53822         }
53823         if(panel.setRegion){
53824             panel.setRegion(this);
53825         }
53826         this.panels.add(panel);
53827         el.setStyle("position", "absolute");
53828         if(!panel.background){
53829             this.setActivePanel(panel);
53830             if(this.config.initialSize && this.panels.getCount()==1){
53831                 this.resizeTo(this.config.initialSize);
53832             }
53833         }
53834         this.fireEvent("paneladded", this, panel);
53835         return panel;
53836     },
53837     
53838     /**
53839      * Returns true if the panel is in this region.
53840      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53841      * @return {Boolean}
53842      */
53843     hasPanel : function(panel){
53844         if(typeof panel == "object"){ // must be panel obj
53845             panel = panel.getId();
53846         }
53847         return this.getPanel(panel) ? true : false;
53848     },
53849     
53850     /**
53851      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53852      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53853      * @param {Boolean} preservePanel Overrides the config preservePanel option
53854      * @return {Roo.ContentPanel} The panel that was removed
53855      */
53856     remove : function(panel, preservePanel){
53857         panel = this.getPanel(panel);
53858         if(!panel){
53859             return null;
53860         }
53861         var e = {};
53862         this.fireEvent("beforeremove", this, panel, e);
53863         if(e.cancel === true){
53864             return null;
53865         }
53866         var panelId = panel.getId();
53867         this.panels.removeKey(panelId);
53868         return panel;
53869     },
53870     
53871     /**
53872      * Returns the panel specified or null if it's not in this region.
53873      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53874      * @return {Roo.ContentPanel}
53875      */
53876     getPanel : function(id){
53877         if(typeof id == "object"){ // must be panel obj
53878             return id;
53879         }
53880         return this.panels.get(id);
53881     },
53882     
53883     /**
53884      * Returns this regions position (north/south/east/west/center).
53885      * @return {String} 
53886      */
53887     getPosition: function(){
53888         return this.position;    
53889     }
53890 });/*
53891  * Based on:
53892  * Ext JS Library 1.1.1
53893  * Copyright(c) 2006-2007, Ext JS, LLC.
53894  *
53895  * Originally Released Under LGPL - original licence link has changed is not relivant.
53896  *
53897  * Fork - LGPL
53898  * <script type="text/javascript">
53899  */
53900  
53901 /**
53902  * @class Roo.LayoutRegion
53903  * @extends Roo.BasicLayoutRegion
53904  * This class represents a region in a layout manager.
53905  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53906  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53907  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53908  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53909  * @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})
53910  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53911  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53912  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53913  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53914  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53915  * @cfg {String}    title           The title for the region (overrides panel titles)
53916  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53917  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53918  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53919  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53920  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53921  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53922  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53923  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53924  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53925  * @cfg {Boolean}   showPin         True to show a pin button
53926  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53927  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53928  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53929  * @cfg {Number}    width           For East/West panels
53930  * @cfg {Number}    height          For North/South panels
53931  * @cfg {Boolean}   split           To show the splitter
53932  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53933  */
53934 Roo.LayoutRegion = function(mgr, config, pos){
53935     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53936     var dh = Roo.DomHelper;
53937     /** This region's container element 
53938     * @type Roo.Element */
53939     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53940     /** This region's title element 
53941     * @type Roo.Element */
53942
53943     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53944         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53945         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53946     ]}, true);
53947     this.titleEl.enableDisplayMode();
53948     /** This region's title text element 
53949     * @type HTMLElement */
53950     this.titleTextEl = this.titleEl.dom.firstChild;
53951     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53952     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53953     this.closeBtn.enableDisplayMode();
53954     this.closeBtn.on("click", this.closeClicked, this);
53955     this.closeBtn.hide();
53956
53957     this.createBody(config);
53958     this.visible = true;
53959     this.collapsed = false;
53960
53961     if(config.hideWhenEmpty){
53962         this.hide();
53963         this.on("paneladded", this.validateVisibility, this);
53964         this.on("panelremoved", this.validateVisibility, this);
53965     }
53966     this.applyConfig(config);
53967 };
53968
53969 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53970
53971     createBody : function(){
53972         /** This region's body element 
53973         * @type Roo.Element */
53974         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53975     },
53976
53977     applyConfig : function(c){
53978         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53979             var dh = Roo.DomHelper;
53980             if(c.titlebar !== false){
53981                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53982                 this.collapseBtn.on("click", this.collapse, this);
53983                 this.collapseBtn.enableDisplayMode();
53984
53985                 if(c.showPin === true || this.showPin){
53986                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53987                     this.stickBtn.enableDisplayMode();
53988                     this.stickBtn.on("click", this.expand, this);
53989                     this.stickBtn.hide();
53990                 }
53991             }
53992             /** This region's collapsed element
53993             * @type Roo.Element */
53994             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53995                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53996             ]}, true);
53997             if(c.floatable !== false){
53998                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53999                this.collapsedEl.on("click", this.collapseClick, this);
54000             }
54001
54002             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54003                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54004                    id: "message", unselectable: "on", style:{"float":"left"}});
54005                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54006              }
54007             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54008             this.expandBtn.on("click", this.expand, this);
54009         }
54010         if(this.collapseBtn){
54011             this.collapseBtn.setVisible(c.collapsible == true);
54012         }
54013         this.cmargins = c.cmargins || this.cmargins ||
54014                          (this.position == "west" || this.position == "east" ?
54015                              {top: 0, left: 2, right:2, bottom: 0} :
54016                              {top: 2, left: 0, right:0, bottom: 2});
54017         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54018         this.bottomTabs = c.tabPosition != "top";
54019         this.autoScroll = c.autoScroll || false;
54020         if(this.autoScroll){
54021             this.bodyEl.setStyle("overflow", "auto");
54022         }else{
54023             this.bodyEl.setStyle("overflow", "hidden");
54024         }
54025         //if(c.titlebar !== false){
54026             if((!c.titlebar && !c.title) || c.titlebar === false){
54027                 this.titleEl.hide();
54028             }else{
54029                 this.titleEl.show();
54030                 if(c.title){
54031                     this.titleTextEl.innerHTML = c.title;
54032                 }
54033             }
54034         //}
54035         this.duration = c.duration || .30;
54036         this.slideDuration = c.slideDuration || .45;
54037         this.config = c;
54038         if(c.collapsed){
54039             this.collapse(true);
54040         }
54041         if(c.hidden){
54042             this.hide();
54043         }
54044     },
54045     /**
54046      * Returns true if this region is currently visible.
54047      * @return {Boolean}
54048      */
54049     isVisible : function(){
54050         return this.visible;
54051     },
54052
54053     /**
54054      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54055      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54056      */
54057     setCollapsedTitle : function(title){
54058         title = title || "&#160;";
54059         if(this.collapsedTitleTextEl){
54060             this.collapsedTitleTextEl.innerHTML = title;
54061         }
54062     },
54063
54064     getBox : function(){
54065         var b;
54066         if(!this.collapsed){
54067             b = this.el.getBox(false, true);
54068         }else{
54069             b = this.collapsedEl.getBox(false, true);
54070         }
54071         return b;
54072     },
54073
54074     getMargins : function(){
54075         return this.collapsed ? this.cmargins : this.margins;
54076     },
54077
54078     highlight : function(){
54079         this.el.addClass("x-layout-panel-dragover");
54080     },
54081
54082     unhighlight : function(){
54083         this.el.removeClass("x-layout-panel-dragover");
54084     },
54085
54086     updateBox : function(box){
54087         this.box = box;
54088         if(!this.collapsed){
54089             this.el.dom.style.left = box.x + "px";
54090             this.el.dom.style.top = box.y + "px";
54091             this.updateBody(box.width, box.height);
54092         }else{
54093             this.collapsedEl.dom.style.left = box.x + "px";
54094             this.collapsedEl.dom.style.top = box.y + "px";
54095             this.collapsedEl.setSize(box.width, box.height);
54096         }
54097         if(this.tabs){
54098             this.tabs.autoSizeTabs();
54099         }
54100     },
54101
54102     updateBody : function(w, h){
54103         if(w !== null){
54104             this.el.setWidth(w);
54105             w -= this.el.getBorderWidth("rl");
54106             if(this.config.adjustments){
54107                 w += this.config.adjustments[0];
54108             }
54109         }
54110         if(h !== null){
54111             this.el.setHeight(h);
54112             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54113             h -= this.el.getBorderWidth("tb");
54114             if(this.config.adjustments){
54115                 h += this.config.adjustments[1];
54116             }
54117             this.bodyEl.setHeight(h);
54118             if(this.tabs){
54119                 h = this.tabs.syncHeight(h);
54120             }
54121         }
54122         if(this.panelSize){
54123             w = w !== null ? w : this.panelSize.width;
54124             h = h !== null ? h : this.panelSize.height;
54125         }
54126         if(this.activePanel){
54127             var el = this.activePanel.getEl();
54128             w = w !== null ? w : el.getWidth();
54129             h = h !== null ? h : el.getHeight();
54130             this.panelSize = {width: w, height: h};
54131             this.activePanel.setSize(w, h);
54132         }
54133         if(Roo.isIE && this.tabs){
54134             this.tabs.el.repaint();
54135         }
54136     },
54137
54138     /**
54139      * Returns the container element for this region.
54140      * @return {Roo.Element}
54141      */
54142     getEl : function(){
54143         return this.el;
54144     },
54145
54146     /**
54147      * Hides this region.
54148      */
54149     hide : function(){
54150         if(!this.collapsed){
54151             this.el.dom.style.left = "-2000px";
54152             this.el.hide();
54153         }else{
54154             this.collapsedEl.dom.style.left = "-2000px";
54155             this.collapsedEl.hide();
54156         }
54157         this.visible = false;
54158         this.fireEvent("visibilitychange", this, false);
54159     },
54160
54161     /**
54162      * Shows this region if it was previously hidden.
54163      */
54164     show : function(){
54165         if(!this.collapsed){
54166             this.el.show();
54167         }else{
54168             this.collapsedEl.show();
54169         }
54170         this.visible = true;
54171         this.fireEvent("visibilitychange", this, true);
54172     },
54173
54174     closeClicked : function(){
54175         if(this.activePanel){
54176             this.remove(this.activePanel);
54177         }
54178     },
54179
54180     collapseClick : function(e){
54181         if(this.isSlid){
54182            e.stopPropagation();
54183            this.slideIn();
54184         }else{
54185            e.stopPropagation();
54186            this.slideOut();
54187         }
54188     },
54189
54190     /**
54191      * Collapses this region.
54192      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54193      */
54194     collapse : function(skipAnim, skipCheck){
54195         if(this.collapsed) {
54196             return;
54197         }
54198         
54199         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54200             
54201             this.collapsed = true;
54202             if(this.split){
54203                 this.split.el.hide();
54204             }
54205             if(this.config.animate && skipAnim !== true){
54206                 this.fireEvent("invalidated", this);
54207                 this.animateCollapse();
54208             }else{
54209                 this.el.setLocation(-20000,-20000);
54210                 this.el.hide();
54211                 this.collapsedEl.show();
54212                 this.fireEvent("collapsed", this);
54213                 this.fireEvent("invalidated", this);
54214             }
54215         }
54216         
54217     },
54218
54219     animateCollapse : function(){
54220         // overridden
54221     },
54222
54223     /**
54224      * Expands this region if it was previously collapsed.
54225      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54226      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54227      */
54228     expand : function(e, skipAnim){
54229         if(e) {
54230             e.stopPropagation();
54231         }
54232         if(!this.collapsed || this.el.hasActiveFx()) {
54233             return;
54234         }
54235         if(this.isSlid){
54236             this.afterSlideIn();
54237             skipAnim = true;
54238         }
54239         this.collapsed = false;
54240         if(this.config.animate && skipAnim !== true){
54241             this.animateExpand();
54242         }else{
54243             this.el.show();
54244             if(this.split){
54245                 this.split.el.show();
54246             }
54247             this.collapsedEl.setLocation(-2000,-2000);
54248             this.collapsedEl.hide();
54249             this.fireEvent("invalidated", this);
54250             this.fireEvent("expanded", this);
54251         }
54252     },
54253
54254     animateExpand : function(){
54255         // overridden
54256     },
54257
54258     initTabs : function()
54259     {
54260         this.bodyEl.setStyle("overflow", "hidden");
54261         var ts = new Roo.TabPanel(
54262                 this.bodyEl.dom,
54263                 {
54264                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54265                     disableTooltips: this.config.disableTabTips,
54266                     toolbar : this.config.toolbar
54267                 }
54268         );
54269         if(this.config.hideTabs){
54270             ts.stripWrap.setDisplayed(false);
54271         }
54272         this.tabs = ts;
54273         ts.resizeTabs = this.config.resizeTabs === true;
54274         ts.minTabWidth = this.config.minTabWidth || 40;
54275         ts.maxTabWidth = this.config.maxTabWidth || 250;
54276         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54277         ts.monitorResize = false;
54278         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54279         ts.bodyEl.addClass('x-layout-tabs-body');
54280         this.panels.each(this.initPanelAsTab, this);
54281     },
54282
54283     initPanelAsTab : function(panel){
54284         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54285                     this.config.closeOnTab && panel.isClosable());
54286         if(panel.tabTip !== undefined){
54287             ti.setTooltip(panel.tabTip);
54288         }
54289         ti.on("activate", function(){
54290               this.setActivePanel(panel);
54291         }, this);
54292         if(this.config.closeOnTab){
54293             ti.on("beforeclose", function(t, e){
54294                 e.cancel = true;
54295                 this.remove(panel);
54296             }, this);
54297         }
54298         return ti;
54299     },
54300
54301     updatePanelTitle : function(panel, title){
54302         if(this.activePanel == panel){
54303             this.updateTitle(title);
54304         }
54305         if(this.tabs){
54306             var ti = this.tabs.getTab(panel.getEl().id);
54307             ti.setText(title);
54308             if(panel.tabTip !== undefined){
54309                 ti.setTooltip(panel.tabTip);
54310             }
54311         }
54312     },
54313
54314     updateTitle : function(title){
54315         if(this.titleTextEl && !this.config.title){
54316             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54317         }
54318     },
54319
54320     setActivePanel : function(panel){
54321         panel = this.getPanel(panel);
54322         if(this.activePanel && this.activePanel != panel){
54323             this.activePanel.setActiveState(false);
54324         }
54325         this.activePanel = panel;
54326         panel.setActiveState(true);
54327         if(this.panelSize){
54328             panel.setSize(this.panelSize.width, this.panelSize.height);
54329         }
54330         if(this.closeBtn){
54331             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54332         }
54333         this.updateTitle(panel.getTitle());
54334         if(this.tabs){
54335             this.fireEvent("invalidated", this);
54336         }
54337         this.fireEvent("panelactivated", this, panel);
54338     },
54339
54340     /**
54341      * Shows the specified panel.
54342      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54343      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54344      */
54345     showPanel : function(panel)
54346     {
54347         panel = this.getPanel(panel);
54348         if(panel){
54349             if(this.tabs){
54350                 var tab = this.tabs.getTab(panel.getEl().id);
54351                 if(tab.isHidden()){
54352                     this.tabs.unhideTab(tab.id);
54353                 }
54354                 tab.activate();
54355             }else{
54356                 this.setActivePanel(panel);
54357             }
54358         }
54359         return panel;
54360     },
54361
54362     /**
54363      * Get the active panel for this region.
54364      * @return {Roo.ContentPanel} The active panel or null
54365      */
54366     getActivePanel : function(){
54367         return this.activePanel;
54368     },
54369
54370     validateVisibility : function(){
54371         if(this.panels.getCount() < 1){
54372             this.updateTitle("&#160;");
54373             this.closeBtn.hide();
54374             this.hide();
54375         }else{
54376             if(!this.isVisible()){
54377                 this.show();
54378             }
54379         }
54380     },
54381
54382     /**
54383      * Adds the passed ContentPanel(s) to this region.
54384      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54385      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54386      */
54387     add : function(panel){
54388         if(arguments.length > 1){
54389             for(var i = 0, len = arguments.length; i < len; i++) {
54390                 this.add(arguments[i]);
54391             }
54392             return null;
54393         }
54394         if(this.hasPanel(panel)){
54395             this.showPanel(panel);
54396             return panel;
54397         }
54398         panel.setRegion(this);
54399         this.panels.add(panel);
54400         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54401             this.bodyEl.dom.appendChild(panel.getEl().dom);
54402             if(panel.background !== true){
54403                 this.setActivePanel(panel);
54404             }
54405             this.fireEvent("paneladded", this, panel);
54406             return panel;
54407         }
54408         if(!this.tabs){
54409             this.initTabs();
54410         }else{
54411             this.initPanelAsTab(panel);
54412         }
54413         if(panel.background !== true){
54414             this.tabs.activate(panel.getEl().id);
54415         }
54416         this.fireEvent("paneladded", this, panel);
54417         return panel;
54418     },
54419
54420     /**
54421      * Hides the tab for the specified panel.
54422      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54423      */
54424     hidePanel : function(panel){
54425         if(this.tabs && (panel = this.getPanel(panel))){
54426             this.tabs.hideTab(panel.getEl().id);
54427         }
54428     },
54429
54430     /**
54431      * Unhides the tab for a previously hidden panel.
54432      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54433      */
54434     unhidePanel : function(panel){
54435         if(this.tabs && (panel = this.getPanel(panel))){
54436             this.tabs.unhideTab(panel.getEl().id);
54437         }
54438     },
54439
54440     clearPanels : function(){
54441         while(this.panels.getCount() > 0){
54442              this.remove(this.panels.first());
54443         }
54444     },
54445
54446     /**
54447      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54448      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54449      * @param {Boolean} preservePanel Overrides the config preservePanel option
54450      * @return {Roo.ContentPanel} The panel that was removed
54451      */
54452     remove : function(panel, preservePanel){
54453         panel = this.getPanel(panel);
54454         if(!panel){
54455             return null;
54456         }
54457         var e = {};
54458         this.fireEvent("beforeremove", this, panel, e);
54459         if(e.cancel === true){
54460             return null;
54461         }
54462         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
54463         var panelId = panel.getId();
54464         this.panels.removeKey(panelId);
54465         if(preservePanel){
54466             document.body.appendChild(panel.getEl().dom);
54467         }
54468         if(this.tabs){
54469             this.tabs.removeTab(panel.getEl().id);
54470         }else if (!preservePanel){
54471             this.bodyEl.dom.removeChild(panel.getEl().dom);
54472         }
54473         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
54474             var p = this.panels.first();
54475             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
54476             tempEl.appendChild(p.getEl().dom);
54477             this.bodyEl.update("");
54478             this.bodyEl.dom.appendChild(p.getEl().dom);
54479             tempEl = null;
54480             this.updateTitle(p.getTitle());
54481             this.tabs = null;
54482             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54483             this.setActivePanel(p);
54484         }
54485         panel.setRegion(null);
54486         if(this.activePanel == panel){
54487             this.activePanel = null;
54488         }
54489         if(this.config.autoDestroy !== false && preservePanel !== true){
54490             try{panel.destroy();}catch(e){}
54491         }
54492         this.fireEvent("panelremoved", this, panel);
54493         return panel;
54494     },
54495
54496     /**
54497      * Returns the TabPanel component used by this region
54498      * @return {Roo.TabPanel}
54499      */
54500     getTabs : function(){
54501         return this.tabs;
54502     },
54503
54504     createTool : function(parentEl, className){
54505         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
54506             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
54507         btn.addClassOnOver("x-layout-tools-button-over");
54508         return btn;
54509     }
54510 });/*
54511  * Based on:
54512  * Ext JS Library 1.1.1
54513  * Copyright(c) 2006-2007, Ext JS, LLC.
54514  *
54515  * Originally Released Under LGPL - original licence link has changed is not relivant.
54516  *
54517  * Fork - LGPL
54518  * <script type="text/javascript">
54519  */
54520  
54521
54522
54523 /**
54524  * @class Roo.SplitLayoutRegion
54525  * @extends Roo.LayoutRegion
54526  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
54527  */
54528 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
54529     this.cursor = cursor;
54530     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
54531 };
54532
54533 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
54534     splitTip : "Drag to resize.",
54535     collapsibleSplitTip : "Drag to resize. Double click to hide.",
54536     useSplitTips : false,
54537
54538     applyConfig : function(config){
54539         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
54540         if(config.split){
54541             if(!this.split){
54542                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
54543                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
54544                 /** The SplitBar for this region 
54545                 * @type Roo.SplitBar */
54546                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
54547                 this.split.on("moved", this.onSplitMove, this);
54548                 this.split.useShim = config.useShim === true;
54549                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
54550                 if(this.useSplitTips){
54551                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
54552                 }
54553                 if(config.collapsible){
54554                     this.split.el.on("dblclick", this.collapse,  this);
54555                 }
54556             }
54557             if(typeof config.minSize != "undefined"){
54558                 this.split.minSize = config.minSize;
54559             }
54560             if(typeof config.maxSize != "undefined"){
54561                 this.split.maxSize = config.maxSize;
54562             }
54563             if(config.hideWhenEmpty || config.hidden || config.collapsed){
54564                 this.hideSplitter();
54565             }
54566         }
54567     },
54568
54569     getHMaxSize : function(){
54570          var cmax = this.config.maxSize || 10000;
54571          var center = this.mgr.getRegion("center");
54572          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
54573     },
54574
54575     getVMaxSize : function(){
54576          var cmax = this.config.maxSize || 10000;
54577          var center = this.mgr.getRegion("center");
54578          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
54579     },
54580
54581     onSplitMove : function(split, newSize){
54582         this.fireEvent("resized", this, newSize);
54583     },
54584     
54585     /** 
54586      * Returns the {@link Roo.SplitBar} for this region.
54587      * @return {Roo.SplitBar}
54588      */
54589     getSplitBar : function(){
54590         return this.split;
54591     },
54592     
54593     hide : function(){
54594         this.hideSplitter();
54595         Roo.SplitLayoutRegion.superclass.hide.call(this);
54596     },
54597
54598     hideSplitter : function(){
54599         if(this.split){
54600             this.split.el.setLocation(-2000,-2000);
54601             this.split.el.hide();
54602         }
54603     },
54604
54605     show : function(){
54606         if(this.split){
54607             this.split.el.show();
54608         }
54609         Roo.SplitLayoutRegion.superclass.show.call(this);
54610     },
54611     
54612     beforeSlide: function(){
54613         if(Roo.isGecko){// firefox overflow auto bug workaround
54614             this.bodyEl.clip();
54615             if(this.tabs) {
54616                 this.tabs.bodyEl.clip();
54617             }
54618             if(this.activePanel){
54619                 this.activePanel.getEl().clip();
54620                 
54621                 if(this.activePanel.beforeSlide){
54622                     this.activePanel.beforeSlide();
54623                 }
54624             }
54625         }
54626     },
54627     
54628     afterSlide : function(){
54629         if(Roo.isGecko){// firefox overflow auto bug workaround
54630             this.bodyEl.unclip();
54631             if(this.tabs) {
54632                 this.tabs.bodyEl.unclip();
54633             }
54634             if(this.activePanel){
54635                 this.activePanel.getEl().unclip();
54636                 if(this.activePanel.afterSlide){
54637                     this.activePanel.afterSlide();
54638                 }
54639             }
54640         }
54641     },
54642
54643     initAutoHide : function(){
54644         if(this.autoHide !== false){
54645             if(!this.autoHideHd){
54646                 var st = new Roo.util.DelayedTask(this.slideIn, this);
54647                 this.autoHideHd = {
54648                     "mouseout": function(e){
54649                         if(!e.within(this.el, true)){
54650                             st.delay(500);
54651                         }
54652                     },
54653                     "mouseover" : function(e){
54654                         st.cancel();
54655                     },
54656                     scope : this
54657                 };
54658             }
54659             this.el.on(this.autoHideHd);
54660         }
54661     },
54662
54663     clearAutoHide : function(){
54664         if(this.autoHide !== false){
54665             this.el.un("mouseout", this.autoHideHd.mouseout);
54666             this.el.un("mouseover", this.autoHideHd.mouseover);
54667         }
54668     },
54669
54670     clearMonitor : function(){
54671         Roo.get(document).un("click", this.slideInIf, this);
54672     },
54673
54674     // these names are backwards but not changed for compat
54675     slideOut : function(){
54676         if(this.isSlid || this.el.hasActiveFx()){
54677             return;
54678         }
54679         this.isSlid = true;
54680         if(this.collapseBtn){
54681             this.collapseBtn.hide();
54682         }
54683         this.closeBtnState = this.closeBtn.getStyle('display');
54684         this.closeBtn.hide();
54685         if(this.stickBtn){
54686             this.stickBtn.show();
54687         }
54688         this.el.show();
54689         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
54690         this.beforeSlide();
54691         this.el.setStyle("z-index", 10001);
54692         this.el.slideIn(this.getSlideAnchor(), {
54693             callback: function(){
54694                 this.afterSlide();
54695                 this.initAutoHide();
54696                 Roo.get(document).on("click", this.slideInIf, this);
54697                 this.fireEvent("slideshow", this);
54698             },
54699             scope: this,
54700             block: true
54701         });
54702     },
54703
54704     afterSlideIn : function(){
54705         this.clearAutoHide();
54706         this.isSlid = false;
54707         this.clearMonitor();
54708         this.el.setStyle("z-index", "");
54709         if(this.collapseBtn){
54710             this.collapseBtn.show();
54711         }
54712         this.closeBtn.setStyle('display', this.closeBtnState);
54713         if(this.stickBtn){
54714             this.stickBtn.hide();
54715         }
54716         this.fireEvent("slidehide", this);
54717     },
54718
54719     slideIn : function(cb){
54720         if(!this.isSlid || this.el.hasActiveFx()){
54721             Roo.callback(cb);
54722             return;
54723         }
54724         this.isSlid = false;
54725         this.beforeSlide();
54726         this.el.slideOut(this.getSlideAnchor(), {
54727             callback: function(){
54728                 this.el.setLeftTop(-10000, -10000);
54729                 this.afterSlide();
54730                 this.afterSlideIn();
54731                 Roo.callback(cb);
54732             },
54733             scope: this,
54734             block: true
54735         });
54736     },
54737     
54738     slideInIf : function(e){
54739         if(!e.within(this.el)){
54740             this.slideIn();
54741         }
54742     },
54743
54744     animateCollapse : function(){
54745         this.beforeSlide();
54746         this.el.setStyle("z-index", 20000);
54747         var anchor = this.getSlideAnchor();
54748         this.el.slideOut(anchor, {
54749             callback : function(){
54750                 this.el.setStyle("z-index", "");
54751                 this.collapsedEl.slideIn(anchor, {duration:.3});
54752                 this.afterSlide();
54753                 this.el.setLocation(-10000,-10000);
54754                 this.el.hide();
54755                 this.fireEvent("collapsed", this);
54756             },
54757             scope: this,
54758             block: true
54759         });
54760     },
54761
54762     animateExpand : function(){
54763         this.beforeSlide();
54764         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54765         this.el.setStyle("z-index", 20000);
54766         this.collapsedEl.hide({
54767             duration:.1
54768         });
54769         this.el.slideIn(this.getSlideAnchor(), {
54770             callback : function(){
54771                 this.el.setStyle("z-index", "");
54772                 this.afterSlide();
54773                 if(this.split){
54774                     this.split.el.show();
54775                 }
54776                 this.fireEvent("invalidated", this);
54777                 this.fireEvent("expanded", this);
54778             },
54779             scope: this,
54780             block: true
54781         });
54782     },
54783
54784     anchors : {
54785         "west" : "left",
54786         "east" : "right",
54787         "north" : "top",
54788         "south" : "bottom"
54789     },
54790
54791     sanchors : {
54792         "west" : "l",
54793         "east" : "r",
54794         "north" : "t",
54795         "south" : "b"
54796     },
54797
54798     canchors : {
54799         "west" : "tl-tr",
54800         "east" : "tr-tl",
54801         "north" : "tl-bl",
54802         "south" : "bl-tl"
54803     },
54804
54805     getAnchor : function(){
54806         return this.anchors[this.position];
54807     },
54808
54809     getCollapseAnchor : function(){
54810         return this.canchors[this.position];
54811     },
54812
54813     getSlideAnchor : function(){
54814         return this.sanchors[this.position];
54815     },
54816
54817     getAlignAdj : function(){
54818         var cm = this.cmargins;
54819         switch(this.position){
54820             case "west":
54821                 return [0, 0];
54822             break;
54823             case "east":
54824                 return [0, 0];
54825             break;
54826             case "north":
54827                 return [0, 0];
54828             break;
54829             case "south":
54830                 return [0, 0];
54831             break;
54832         }
54833     },
54834
54835     getExpandAdj : function(){
54836         var c = this.collapsedEl, cm = this.cmargins;
54837         switch(this.position){
54838             case "west":
54839                 return [-(cm.right+c.getWidth()+cm.left), 0];
54840             break;
54841             case "east":
54842                 return [cm.right+c.getWidth()+cm.left, 0];
54843             break;
54844             case "north":
54845                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54846             break;
54847             case "south":
54848                 return [0, cm.top+cm.bottom+c.getHeight()];
54849             break;
54850         }
54851     }
54852 });/*
54853  * Based on:
54854  * Ext JS Library 1.1.1
54855  * Copyright(c) 2006-2007, Ext JS, LLC.
54856  *
54857  * Originally Released Under LGPL - original licence link has changed is not relivant.
54858  *
54859  * Fork - LGPL
54860  * <script type="text/javascript">
54861  */
54862 /*
54863  * These classes are private internal classes
54864  */
54865 Roo.CenterLayoutRegion = function(mgr, config){
54866     Roo.LayoutRegion.call(this, mgr, config, "center");
54867     this.visible = true;
54868     this.minWidth = config.minWidth || 20;
54869     this.minHeight = config.minHeight || 20;
54870 };
54871
54872 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54873     hide : function(){
54874         // center panel can't be hidden
54875     },
54876     
54877     show : function(){
54878         // center panel can't be hidden
54879     },
54880     
54881     getMinWidth: function(){
54882         return this.minWidth;
54883     },
54884     
54885     getMinHeight: function(){
54886         return this.minHeight;
54887     }
54888 });
54889
54890
54891 Roo.NorthLayoutRegion = function(mgr, config){
54892     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54893     if(this.split){
54894         this.split.placement = Roo.SplitBar.TOP;
54895         this.split.orientation = Roo.SplitBar.VERTICAL;
54896         this.split.el.addClass("x-layout-split-v");
54897     }
54898     var size = config.initialSize || config.height;
54899     if(typeof size != "undefined"){
54900         this.el.setHeight(size);
54901     }
54902 };
54903 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54904     orientation: Roo.SplitBar.VERTICAL,
54905     getBox : function(){
54906         if(this.collapsed){
54907             return this.collapsedEl.getBox();
54908         }
54909         var box = this.el.getBox();
54910         if(this.split){
54911             box.height += this.split.el.getHeight();
54912         }
54913         return box;
54914     },
54915     
54916     updateBox : function(box){
54917         if(this.split && !this.collapsed){
54918             box.height -= this.split.el.getHeight();
54919             this.split.el.setLeft(box.x);
54920             this.split.el.setTop(box.y+box.height);
54921             this.split.el.setWidth(box.width);
54922         }
54923         if(this.collapsed){
54924             this.updateBody(box.width, null);
54925         }
54926         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54927     }
54928 });
54929
54930 Roo.SouthLayoutRegion = function(mgr, config){
54931     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54932     if(this.split){
54933         this.split.placement = Roo.SplitBar.BOTTOM;
54934         this.split.orientation = Roo.SplitBar.VERTICAL;
54935         this.split.el.addClass("x-layout-split-v");
54936     }
54937     var size = config.initialSize || config.height;
54938     if(typeof size != "undefined"){
54939         this.el.setHeight(size);
54940     }
54941 };
54942 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54943     orientation: Roo.SplitBar.VERTICAL,
54944     getBox : function(){
54945         if(this.collapsed){
54946             return this.collapsedEl.getBox();
54947         }
54948         var box = this.el.getBox();
54949         if(this.split){
54950             var sh = this.split.el.getHeight();
54951             box.height += sh;
54952             box.y -= sh;
54953         }
54954         return box;
54955     },
54956     
54957     updateBox : function(box){
54958         if(this.split && !this.collapsed){
54959             var sh = this.split.el.getHeight();
54960             box.height -= sh;
54961             box.y += sh;
54962             this.split.el.setLeft(box.x);
54963             this.split.el.setTop(box.y-sh);
54964             this.split.el.setWidth(box.width);
54965         }
54966         if(this.collapsed){
54967             this.updateBody(box.width, null);
54968         }
54969         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54970     }
54971 });
54972
54973 Roo.EastLayoutRegion = function(mgr, config){
54974     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54975     if(this.split){
54976         this.split.placement = Roo.SplitBar.RIGHT;
54977         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54978         this.split.el.addClass("x-layout-split-h");
54979     }
54980     var size = config.initialSize || config.width;
54981     if(typeof size != "undefined"){
54982         this.el.setWidth(size);
54983     }
54984 };
54985 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54986     orientation: Roo.SplitBar.HORIZONTAL,
54987     getBox : function(){
54988         if(this.collapsed){
54989             return this.collapsedEl.getBox();
54990         }
54991         var box = this.el.getBox();
54992         if(this.split){
54993             var sw = this.split.el.getWidth();
54994             box.width += sw;
54995             box.x -= sw;
54996         }
54997         return box;
54998     },
54999
55000     updateBox : function(box){
55001         if(this.split && !this.collapsed){
55002             var sw = this.split.el.getWidth();
55003             box.width -= sw;
55004             this.split.el.setLeft(box.x);
55005             this.split.el.setTop(box.y);
55006             this.split.el.setHeight(box.height);
55007             box.x += sw;
55008         }
55009         if(this.collapsed){
55010             this.updateBody(null, box.height);
55011         }
55012         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55013     }
55014 });
55015
55016 Roo.WestLayoutRegion = function(mgr, config){
55017     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55018     if(this.split){
55019         this.split.placement = Roo.SplitBar.LEFT;
55020         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55021         this.split.el.addClass("x-layout-split-h");
55022     }
55023     var size = config.initialSize || config.width;
55024     if(typeof size != "undefined"){
55025         this.el.setWidth(size);
55026     }
55027 };
55028 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55029     orientation: Roo.SplitBar.HORIZONTAL,
55030     getBox : function(){
55031         if(this.collapsed){
55032             return this.collapsedEl.getBox();
55033         }
55034         var box = this.el.getBox();
55035         if(this.split){
55036             box.width += this.split.el.getWidth();
55037         }
55038         return box;
55039     },
55040     
55041     updateBox : function(box){
55042         if(this.split && !this.collapsed){
55043             var sw = this.split.el.getWidth();
55044             box.width -= sw;
55045             this.split.el.setLeft(box.x+box.width);
55046             this.split.el.setTop(box.y);
55047             this.split.el.setHeight(box.height);
55048         }
55049         if(this.collapsed){
55050             this.updateBody(null, box.height);
55051         }
55052         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55053     }
55054 });
55055 /*
55056  * Based on:
55057  * Ext JS Library 1.1.1
55058  * Copyright(c) 2006-2007, Ext JS, LLC.
55059  *
55060  * Originally Released Under LGPL - original licence link has changed is not relivant.
55061  *
55062  * Fork - LGPL
55063  * <script type="text/javascript">
55064  */
55065  
55066  
55067 /*
55068  * Private internal class for reading and applying state
55069  */
55070 Roo.LayoutStateManager = function(layout){
55071      // default empty state
55072      this.state = {
55073         north: {},
55074         south: {},
55075         east: {},
55076         west: {}       
55077     };
55078 };
55079
55080 Roo.LayoutStateManager.prototype = {
55081     init : function(layout, provider){
55082         this.provider = provider;
55083         var state = provider.get(layout.id+"-layout-state");
55084         if(state){
55085             var wasUpdating = layout.isUpdating();
55086             if(!wasUpdating){
55087                 layout.beginUpdate();
55088             }
55089             for(var key in state){
55090                 if(typeof state[key] != "function"){
55091                     var rstate = state[key];
55092                     var r = layout.getRegion(key);
55093                     if(r && rstate){
55094                         if(rstate.size){
55095                             r.resizeTo(rstate.size);
55096                         }
55097                         if(rstate.collapsed == true){
55098                             r.collapse(true);
55099                         }else{
55100                             r.expand(null, true);
55101                         }
55102                     }
55103                 }
55104             }
55105             if(!wasUpdating){
55106                 layout.endUpdate();
55107             }
55108             this.state = state; 
55109         }
55110         this.layout = layout;
55111         layout.on("regionresized", this.onRegionResized, this);
55112         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55113         layout.on("regionexpanded", this.onRegionExpanded, this);
55114     },
55115     
55116     storeState : function(){
55117         this.provider.set(this.layout.id+"-layout-state", this.state);
55118     },
55119     
55120     onRegionResized : function(region, newSize){
55121         this.state[region.getPosition()].size = newSize;
55122         this.storeState();
55123     },
55124     
55125     onRegionCollapsed : function(region){
55126         this.state[region.getPosition()].collapsed = true;
55127         this.storeState();
55128     },
55129     
55130     onRegionExpanded : function(region){
55131         this.state[region.getPosition()].collapsed = false;
55132         this.storeState();
55133     }
55134 };/*
55135  * Based on:
55136  * Ext JS Library 1.1.1
55137  * Copyright(c) 2006-2007, Ext JS, LLC.
55138  *
55139  * Originally Released Under LGPL - original licence link has changed is not relivant.
55140  *
55141  * Fork - LGPL
55142  * <script type="text/javascript">
55143  */
55144 /**
55145  * @class Roo.ContentPanel
55146  * @extends Roo.util.Observable
55147  * @children Roo.form.Form Roo.JsonView Roo.View
55148  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55149  * A basic ContentPanel element.
55150  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55151  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55152  * @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
55153  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55154  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55155  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55156  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55157  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55158  * @cfg {String} title          The title for this panel
55159  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55160  * @cfg {String} url            Calls {@link #setUrl} with this value
55161  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55162  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55163  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55164  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55165  * @cfg {String}    style  Extra style to add to the content panel
55166  * @cfg {Roo.menu.Menu} menu  popup menu
55167
55168  * @constructor
55169  * Create a new ContentPanel.
55170  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55171  * @param {String/Object} config A string to set only the title or a config object
55172  * @param {String} content (optional) Set the HTML content for this panel
55173  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55174  */
55175 Roo.ContentPanel = function(el, config, content){
55176     
55177      
55178     /*
55179     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55180         config = el;
55181         el = Roo.id();
55182     }
55183     if (config && config.parentLayout) { 
55184         el = config.parentLayout.el.createChild(); 
55185     }
55186     */
55187     if(el.autoCreate){ // xtype is available if this is called from factory
55188         config = el;
55189         el = Roo.id();
55190     }
55191     this.el = Roo.get(el);
55192     if(!this.el && config && config.autoCreate){
55193         if(typeof config.autoCreate == "object"){
55194             if(!config.autoCreate.id){
55195                 config.autoCreate.id = config.id||el;
55196             }
55197             this.el = Roo.DomHelper.append(document.body,
55198                         config.autoCreate, true);
55199         }else{
55200             this.el = Roo.DomHelper.append(document.body,
55201                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55202         }
55203     }
55204     
55205     
55206     this.closable = false;
55207     this.loaded = false;
55208     this.active = false;
55209     if(typeof config == "string"){
55210         this.title = config;
55211     }else{
55212         Roo.apply(this, config);
55213     }
55214     
55215     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55216         this.wrapEl = this.el.wrap();
55217         this.toolbar.container = this.el.insertSibling(false, 'before');
55218         this.toolbar = new Roo.Toolbar(this.toolbar);
55219     }
55220     
55221     // xtype created footer. - not sure if will work as we normally have to render first..
55222     if (this.footer && !this.footer.el && this.footer.xtype) {
55223         if (!this.wrapEl) {
55224             this.wrapEl = this.el.wrap();
55225         }
55226     
55227         this.footer.container = this.wrapEl.createChild();
55228          
55229         this.footer = Roo.factory(this.footer, Roo);
55230         
55231     }
55232     
55233     if(this.resizeEl){
55234         this.resizeEl = Roo.get(this.resizeEl, true);
55235     }else{
55236         this.resizeEl = this.el;
55237     }
55238     // handle view.xtype
55239     
55240  
55241     
55242     
55243     this.addEvents({
55244         /**
55245          * @event activate
55246          * Fires when this panel is activated. 
55247          * @param {Roo.ContentPanel} this
55248          */
55249         "activate" : true,
55250         /**
55251          * @event deactivate
55252          * Fires when this panel is activated. 
55253          * @param {Roo.ContentPanel} this
55254          */
55255         "deactivate" : true,
55256
55257         /**
55258          * @event resize
55259          * Fires when this panel is resized if fitToFrame is true.
55260          * @param {Roo.ContentPanel} this
55261          * @param {Number} width The width after any component adjustments
55262          * @param {Number} height The height after any component adjustments
55263          */
55264         "resize" : true,
55265         
55266          /**
55267          * @event render
55268          * Fires when this tab is created
55269          * @param {Roo.ContentPanel} this
55270          */
55271         "render" : true
55272          
55273         
55274     });
55275     
55276
55277     
55278     
55279     if(this.autoScroll){
55280         this.resizeEl.setStyle("overflow", "auto");
55281     } else {
55282         // fix randome scrolling
55283         this.el.on('scroll', function() {
55284             Roo.log('fix random scolling');
55285             this.scrollTo('top',0); 
55286         });
55287     }
55288     content = content || this.content;
55289     if(content){
55290         this.setContent(content);
55291     }
55292     if(config && config.url){
55293         this.setUrl(this.url, this.params, this.loadOnce);
55294     }
55295     
55296     
55297     
55298     Roo.ContentPanel.superclass.constructor.call(this);
55299     
55300     if (this.view && typeof(this.view.xtype) != 'undefined') {
55301         this.view.el = this.el.appendChild(document.createElement("div"));
55302         this.view = Roo.factory(this.view); 
55303         this.view.render  &&  this.view.render(false, '');  
55304     }
55305     
55306     
55307     this.fireEvent('render', this);
55308 };
55309
55310 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55311     tabTip:'',
55312     setRegion : function(region){
55313         this.region = region;
55314         if(region){
55315            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55316         }else{
55317            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55318         } 
55319     },
55320     
55321     /**
55322      * Returns the toolbar for this Panel if one was configured. 
55323      * @return {Roo.Toolbar} 
55324      */
55325     getToolbar : function(){
55326         return this.toolbar;
55327     },
55328     
55329     setActiveState : function(active){
55330         this.active = active;
55331         if(!active){
55332             this.fireEvent("deactivate", this);
55333         }else{
55334             this.fireEvent("activate", this);
55335         }
55336     },
55337     /**
55338      * Updates this panel's element
55339      * @param {String} content The new content
55340      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55341     */
55342     setContent : function(content, loadScripts){
55343         this.el.update(content, loadScripts);
55344     },
55345
55346     ignoreResize : function(w, h){
55347         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55348             return true;
55349         }else{
55350             this.lastSize = {width: w, height: h};
55351             return false;
55352         }
55353     },
55354     /**
55355      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55356      * @return {Roo.UpdateManager} The UpdateManager
55357      */
55358     getUpdateManager : function(){
55359         return this.el.getUpdateManager();
55360     },
55361      /**
55362      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55363      * @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:
55364 <pre><code>
55365 panel.load({
55366     url: "your-url.php",
55367     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55368     callback: yourFunction,
55369     scope: yourObject, //(optional scope)
55370     discardUrl: false,
55371     nocache: false,
55372     text: "Loading...",
55373     timeout: 30,
55374     scripts: false
55375 });
55376 </code></pre>
55377      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55378      * 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.
55379      * @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}
55380      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55381      * @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.
55382      * @return {Roo.ContentPanel} this
55383      */
55384     load : function(){
55385         var um = this.el.getUpdateManager();
55386         um.update.apply(um, arguments);
55387         return this;
55388     },
55389
55390
55391     /**
55392      * 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.
55393      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55394      * @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)
55395      * @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)
55396      * @return {Roo.UpdateManager} The UpdateManager
55397      */
55398     setUrl : function(url, params, loadOnce){
55399         if(this.refreshDelegate){
55400             this.removeListener("activate", this.refreshDelegate);
55401         }
55402         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55403         this.on("activate", this.refreshDelegate);
55404         return this.el.getUpdateManager();
55405     },
55406     
55407     _handleRefresh : function(url, params, loadOnce){
55408         if(!loadOnce || !this.loaded){
55409             var updater = this.el.getUpdateManager();
55410             updater.update(url, params, this._setLoaded.createDelegate(this));
55411         }
55412     },
55413     
55414     _setLoaded : function(){
55415         this.loaded = true;
55416     }, 
55417     
55418     /**
55419      * Returns this panel's id
55420      * @return {String} 
55421      */
55422     getId : function(){
55423         return this.el.id;
55424     },
55425     
55426     /** 
55427      * Returns this panel's element - used by regiosn to add.
55428      * @return {Roo.Element} 
55429      */
55430     getEl : function(){
55431         return this.wrapEl || this.el;
55432     },
55433     
55434     adjustForComponents : function(width, height)
55435     {
55436         //Roo.log('adjustForComponents ');
55437         if(this.resizeEl != this.el){
55438             width -= this.el.getFrameWidth('lr');
55439             height -= this.el.getFrameWidth('tb');
55440         }
55441         if(this.toolbar){
55442             var te = this.toolbar.getEl();
55443             height -= te.getHeight();
55444             te.setWidth(width);
55445         }
55446         if(this.footer){
55447             var te = this.footer.getEl();
55448             //Roo.log("footer:" + te.getHeight());
55449             
55450             height -= te.getHeight();
55451             te.setWidth(width);
55452         }
55453         
55454         
55455         if(this.adjustments){
55456             width += this.adjustments[0];
55457             height += this.adjustments[1];
55458         }
55459         return {"width": width, "height": height};
55460     },
55461     
55462     setSize : function(width, height){
55463         if(this.fitToFrame && !this.ignoreResize(width, height)){
55464             if(this.fitContainer && this.resizeEl != this.el){
55465                 this.el.setSize(width, height);
55466             }
55467             var size = this.adjustForComponents(width, height);
55468             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
55469             this.fireEvent('resize', this, size.width, size.height);
55470         }
55471     },
55472     
55473     /**
55474      * Returns this panel's title
55475      * @return {String} 
55476      */
55477     getTitle : function(){
55478         return this.title;
55479     },
55480     
55481     /**
55482      * Set this panel's title
55483      * @param {String} title
55484      */
55485     setTitle : function(title){
55486         this.title = title;
55487         if(this.region){
55488             this.region.updatePanelTitle(this, title);
55489         }
55490     },
55491     
55492     /**
55493      * Returns true is this panel was configured to be closable
55494      * @return {Boolean} 
55495      */
55496     isClosable : function(){
55497         return this.closable;
55498     },
55499     
55500     beforeSlide : function(){
55501         this.el.clip();
55502         this.resizeEl.clip();
55503     },
55504     
55505     afterSlide : function(){
55506         this.el.unclip();
55507         this.resizeEl.unclip();
55508     },
55509     
55510     /**
55511      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
55512      *   Will fail silently if the {@link #setUrl} method has not been called.
55513      *   This does not activate the panel, just updates its content.
55514      */
55515     refresh : function(){
55516         if(this.refreshDelegate){
55517            this.loaded = false;
55518            this.refreshDelegate();
55519         }
55520     },
55521     
55522     /**
55523      * Destroys this panel
55524      */
55525     destroy : function(){
55526         this.el.removeAllListeners();
55527         var tempEl = document.createElement("span");
55528         tempEl.appendChild(this.el.dom);
55529         tempEl.innerHTML = "";
55530         this.el.remove();
55531         this.el = null;
55532     },
55533     
55534     /**
55535      * form - if the content panel contains a form - this is a reference to it.
55536      * @type {Roo.form.Form}
55537      */
55538     form : false,
55539     /**
55540      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
55541      *    This contains a reference to it.
55542      * @type {Roo.View}
55543      */
55544     view : false,
55545     
55546       /**
55547      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
55548      * <pre><code>
55549
55550 layout.addxtype({
55551        xtype : 'Form',
55552        items: [ .... ]
55553    }
55554 );
55555
55556 </code></pre>
55557      * @param {Object} cfg Xtype definition of item to add.
55558      */
55559     
55560     addxtype : function(cfg) {
55561         // add form..
55562         if (cfg.xtype.match(/^Form$/)) {
55563             
55564             var el;
55565             //if (this.footer) {
55566             //    el = this.footer.container.insertSibling(false, 'before');
55567             //} else {
55568                 el = this.el.createChild();
55569             //}
55570
55571             this.form = new  Roo.form.Form(cfg);
55572             
55573             
55574             if ( this.form.allItems.length) {
55575                 this.form.render(el.dom);
55576             }
55577             return this.form;
55578         }
55579         // should only have one of theses..
55580         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
55581             // views.. should not be just added - used named prop 'view''
55582             
55583             cfg.el = this.el.appendChild(document.createElement("div"));
55584             // factory?
55585             
55586             var ret = new Roo.factory(cfg);
55587              
55588              ret.render && ret.render(false, ''); // render blank..
55589             this.view = ret;
55590             return ret;
55591         }
55592         return false;
55593     }
55594 });
55595
55596 /**
55597  * @class Roo.GridPanel
55598  * @extends Roo.ContentPanel
55599  * @constructor
55600  * Create a new GridPanel.
55601  * @param {Roo.grid.Grid} grid The grid for this panel
55602  * @param {String/Object} config A string to set only the panel's title, or a config object
55603  */
55604 Roo.GridPanel = function(grid, config){
55605     
55606   
55607     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
55608         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
55609         
55610     this.wrapper.dom.appendChild(grid.getGridEl().dom);
55611     
55612     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
55613     
55614     if(this.toolbar){
55615         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
55616     }
55617     // xtype created footer. - not sure if will work as we normally have to render first..
55618     if (this.footer && !this.footer.el && this.footer.xtype) {
55619         
55620         this.footer.container = this.grid.getView().getFooterPanel(true);
55621         this.footer.dataSource = this.grid.dataSource;
55622         this.footer = Roo.factory(this.footer, Roo);
55623         
55624     }
55625     
55626     grid.monitorWindowResize = false; // turn off autosizing
55627     grid.autoHeight = false;
55628     grid.autoWidth = false;
55629     this.grid = grid;
55630     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
55631 };
55632
55633 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
55634     getId : function(){
55635         return this.grid.id;
55636     },
55637     
55638     /**
55639      * Returns the grid for this panel
55640      * @return {Roo.grid.Grid} 
55641      */
55642     getGrid : function(){
55643         return this.grid;    
55644     },
55645     
55646     setSize : function(width, height){
55647         if(!this.ignoreResize(width, height)){
55648             var grid = this.grid;
55649             var size = this.adjustForComponents(width, height);
55650             grid.getGridEl().setSize(size.width, size.height);
55651             grid.autoSize();
55652         }
55653     },
55654     
55655     beforeSlide : function(){
55656         this.grid.getView().scroller.clip();
55657     },
55658     
55659     afterSlide : function(){
55660         this.grid.getView().scroller.unclip();
55661     },
55662     
55663     destroy : function(){
55664         this.grid.destroy();
55665         delete this.grid;
55666         Roo.GridPanel.superclass.destroy.call(this); 
55667     }
55668 });
55669
55670
55671 /**
55672  * @class Roo.NestedLayoutPanel
55673  * @extends Roo.ContentPanel
55674  * @constructor
55675  * Create a new NestedLayoutPanel.
55676  * 
55677  * 
55678  * @param {Roo.BorderLayout} layout [required] The layout for this panel
55679  * @param {String/Object} config A string to set only the title or a config object
55680  */
55681 Roo.NestedLayoutPanel = function(layout, config)
55682 {
55683     // construct with only one argument..
55684     /* FIXME - implement nicer consturctors
55685     if (layout.layout) {
55686         config = layout;
55687         layout = config.layout;
55688         delete config.layout;
55689     }
55690     if (layout.xtype && !layout.getEl) {
55691         // then layout needs constructing..
55692         layout = Roo.factory(layout, Roo);
55693     }
55694     */
55695     
55696     
55697     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
55698     
55699     layout.monitorWindowResize = false; // turn off autosizing
55700     this.layout = layout;
55701     this.layout.getEl().addClass("x-layout-nested-layout");
55702     
55703     
55704     
55705     
55706 };
55707
55708 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
55709
55710     setSize : function(width, height){
55711         if(!this.ignoreResize(width, height)){
55712             var size = this.adjustForComponents(width, height);
55713             var el = this.layout.getEl();
55714             el.setSize(size.width, size.height);
55715             var touch = el.dom.offsetWidth;
55716             this.layout.layout();
55717             // ie requires a double layout on the first pass
55718             if(Roo.isIE && !this.initialized){
55719                 this.initialized = true;
55720                 this.layout.layout();
55721             }
55722         }
55723     },
55724     
55725     // activate all subpanels if not currently active..
55726     
55727     setActiveState : function(active){
55728         this.active = active;
55729         if(!active){
55730             this.fireEvent("deactivate", this);
55731             return;
55732         }
55733         
55734         this.fireEvent("activate", this);
55735         // not sure if this should happen before or after..
55736         if (!this.layout) {
55737             return; // should not happen..
55738         }
55739         var reg = false;
55740         for (var r in this.layout.regions) {
55741             reg = this.layout.getRegion(r);
55742             if (reg.getActivePanel()) {
55743                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55744                 reg.setActivePanel(reg.getActivePanel());
55745                 continue;
55746             }
55747             if (!reg.panels.length) {
55748                 continue;
55749             }
55750             reg.showPanel(reg.getPanel(0));
55751         }
55752         
55753         
55754         
55755         
55756     },
55757     
55758     /**
55759      * Returns the nested BorderLayout for this panel
55760      * @return {Roo.BorderLayout} 
55761      */
55762     getLayout : function(){
55763         return this.layout;
55764     },
55765     
55766      /**
55767      * Adds a xtype elements to the layout of the nested panel
55768      * <pre><code>
55769
55770 panel.addxtype({
55771        xtype : 'ContentPanel',
55772        region: 'west',
55773        items: [ .... ]
55774    }
55775 );
55776
55777 panel.addxtype({
55778         xtype : 'NestedLayoutPanel',
55779         region: 'west',
55780         layout: {
55781            center: { },
55782            west: { }   
55783         },
55784         items : [ ... list of content panels or nested layout panels.. ]
55785    }
55786 );
55787 </code></pre>
55788      * @param {Object} cfg Xtype definition of item to add.
55789      */
55790     addxtype : function(cfg) {
55791         return this.layout.addxtype(cfg);
55792     
55793     }
55794 });
55795
55796 Roo.ScrollPanel = function(el, config, content){
55797     config = config || {};
55798     config.fitToFrame = true;
55799     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55800     
55801     this.el.dom.style.overflow = "hidden";
55802     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55803     this.el.removeClass("x-layout-inactive-content");
55804     this.el.on("mousewheel", this.onWheel, this);
55805
55806     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55807     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55808     up.unselectable(); down.unselectable();
55809     up.on("click", this.scrollUp, this);
55810     down.on("click", this.scrollDown, this);
55811     up.addClassOnOver("x-scroller-btn-over");
55812     down.addClassOnOver("x-scroller-btn-over");
55813     up.addClassOnClick("x-scroller-btn-click");
55814     down.addClassOnClick("x-scroller-btn-click");
55815     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55816
55817     this.resizeEl = this.el;
55818     this.el = wrap; this.up = up; this.down = down;
55819 };
55820
55821 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55822     increment : 100,
55823     wheelIncrement : 5,
55824     scrollUp : function(){
55825         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55826     },
55827
55828     scrollDown : function(){
55829         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55830     },
55831
55832     afterScroll : function(){
55833         var el = this.resizeEl;
55834         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55835         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55836         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55837     },
55838
55839     setSize : function(){
55840         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55841         this.afterScroll();
55842     },
55843
55844     onWheel : function(e){
55845         var d = e.getWheelDelta();
55846         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55847         this.afterScroll();
55848         e.stopEvent();
55849     },
55850
55851     setContent : function(content, loadScripts){
55852         this.resizeEl.update(content, loadScripts);
55853     }
55854
55855 });
55856
55857
55858
55859 /**
55860  * @class Roo.TreePanel
55861  * @extends Roo.ContentPanel
55862  * Treepanel component
55863  * 
55864  * @constructor
55865  * Create a new TreePanel. - defaults to fit/scoll contents.
55866  * @param {String/Object} config A string to set only the panel's title, or a config object
55867  */
55868 Roo.TreePanel = function(config){
55869     var el = config.el;
55870     var tree = config.tree;
55871     delete config.tree; 
55872     delete config.el; // hopefull!
55873     
55874     // wrapper for IE7 strict & safari scroll issue
55875     
55876     var treeEl = el.createChild();
55877     config.resizeEl = treeEl;
55878     
55879     
55880     
55881     Roo.TreePanel.superclass.constructor.call(this, el, config);
55882  
55883  
55884     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55885     //console.log(tree);
55886     this.on('activate', function()
55887     {
55888         if (this.tree.rendered) {
55889             return;
55890         }
55891         //console.log('render tree');
55892         this.tree.render();
55893     });
55894     // this should not be needed.. - it's actually the 'el' that resizes?
55895     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55896     
55897     //this.on('resize',  function (cp, w, h) {
55898     //        this.tree.innerCt.setWidth(w);
55899     //        this.tree.innerCt.setHeight(h);
55900     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55901     //});
55902
55903         
55904     
55905 };
55906
55907 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55908     fitToFrame : true,
55909     autoScroll : true,
55910     /*
55911      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
55912      */
55913     tree : false
55914
55915 });
55916
55917
55918
55919
55920
55921
55922
55923
55924
55925
55926
55927 /*
55928  * Based on:
55929  * Ext JS Library 1.1.1
55930  * Copyright(c) 2006-2007, Ext JS, LLC.
55931  *
55932  * Originally Released Under LGPL - original licence link has changed is not relivant.
55933  *
55934  * Fork - LGPL
55935  * <script type="text/javascript">
55936  */
55937  
55938
55939 /**
55940  * @class Roo.ReaderLayout
55941  * @extends Roo.BorderLayout
55942  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55943  * center region containing two nested regions (a top one for a list view and one for item preview below),
55944  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55945  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55946  * expedites the setup of the overall layout and regions for this common application style.
55947  * Example:
55948  <pre><code>
55949 var reader = new Roo.ReaderLayout();
55950 var CP = Roo.ContentPanel;  // shortcut for adding
55951
55952 reader.beginUpdate();
55953 reader.add("north", new CP("north", "North"));
55954 reader.add("west", new CP("west", {title: "West"}));
55955 reader.add("east", new CP("east", {title: "East"}));
55956
55957 reader.regions.listView.add(new CP("listView", "List"));
55958 reader.regions.preview.add(new CP("preview", "Preview"));
55959 reader.endUpdate();
55960 </code></pre>
55961 * @constructor
55962 * Create a new ReaderLayout
55963 * @param {Object} config Configuration options
55964 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55965 * document.body if omitted)
55966 */
55967 Roo.ReaderLayout = function(config, renderTo){
55968     var c = config || {size:{}};
55969     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55970         north: c.north !== false ? Roo.apply({
55971             split:false,
55972             initialSize: 32,
55973             titlebar: false
55974         }, c.north) : false,
55975         west: c.west !== false ? Roo.apply({
55976             split:true,
55977             initialSize: 200,
55978             minSize: 175,
55979             maxSize: 400,
55980             titlebar: true,
55981             collapsible: true,
55982             animate: true,
55983             margins:{left:5,right:0,bottom:5,top:5},
55984             cmargins:{left:5,right:5,bottom:5,top:5}
55985         }, c.west) : false,
55986         east: c.east !== false ? Roo.apply({
55987             split:true,
55988             initialSize: 200,
55989             minSize: 175,
55990             maxSize: 400,
55991             titlebar: true,
55992             collapsible: true,
55993             animate: true,
55994             margins:{left:0,right:5,bottom:5,top:5},
55995             cmargins:{left:5,right:5,bottom:5,top:5}
55996         }, c.east) : false,
55997         center: Roo.apply({
55998             tabPosition: 'top',
55999             autoScroll:false,
56000             closeOnTab: true,
56001             titlebar:false,
56002             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56003         }, c.center)
56004     });
56005
56006     this.el.addClass('x-reader');
56007
56008     this.beginUpdate();
56009
56010     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56011         south: c.preview !== false ? Roo.apply({
56012             split:true,
56013             initialSize: 200,
56014             minSize: 100,
56015             autoScroll:true,
56016             collapsible:true,
56017             titlebar: true,
56018             cmargins:{top:5,left:0, right:0, bottom:0}
56019         }, c.preview) : false,
56020         center: Roo.apply({
56021             autoScroll:false,
56022             titlebar:false,
56023             minHeight:200
56024         }, c.listView)
56025     });
56026     this.add('center', new Roo.NestedLayoutPanel(inner,
56027             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56028
56029     this.endUpdate();
56030
56031     this.regions.preview = inner.getRegion('south');
56032     this.regions.listView = inner.getRegion('center');
56033 };
56034
56035 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56036  * Based on:
56037  * Ext JS Library 1.1.1
56038  * Copyright(c) 2006-2007, Ext JS, LLC.
56039  *
56040  * Originally Released Under LGPL - original licence link has changed is not relivant.
56041  *
56042  * Fork - LGPL
56043  * <script type="text/javascript">
56044  */
56045  
56046 /**
56047  * @class Roo.grid.Grid
56048  * @extends Roo.util.Observable
56049  * This class represents the primary interface of a component based grid control.
56050  * <br><br>Usage:<pre><code>
56051  var grid = new Roo.grid.Grid("my-container-id", {
56052      ds: myDataStore,
56053      cm: myColModel,
56054      selModel: mySelectionModel,
56055      autoSizeColumns: true,
56056      monitorWindowResize: false,
56057      trackMouseOver: true
56058  });
56059  // set any options
56060  grid.render();
56061  * </code></pre>
56062  * <b>Common Problems:</b><br/>
56063  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56064  * element will correct this<br/>
56065  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56066  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56067  * are unpredictable.<br/>
56068  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56069  * grid to calculate dimensions/offsets.<br/>
56070   * @constructor
56071  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56072  * The container MUST have some type of size defined for the grid to fill. The container will be
56073  * automatically set to position relative if it isn't already.
56074  * @param {Object} config A config object that sets properties on this grid.
56075  */
56076 Roo.grid.Grid = function(container, config){
56077         // initialize the container
56078         this.container = Roo.get(container);
56079         this.container.update("");
56080         this.container.setStyle("overflow", "hidden");
56081     this.container.addClass('x-grid-container');
56082
56083     this.id = this.container.id;
56084
56085     Roo.apply(this, config);
56086     // check and correct shorthanded configs
56087     if(this.ds){
56088         this.dataSource = this.ds;
56089         delete this.ds;
56090     }
56091     if(this.cm){
56092         this.colModel = this.cm;
56093         delete this.cm;
56094     }
56095     if(this.sm){
56096         this.selModel = this.sm;
56097         delete this.sm;
56098     }
56099
56100     if (this.selModel) {
56101         this.selModel = Roo.factory(this.selModel, Roo.grid);
56102         this.sm = this.selModel;
56103         this.sm.xmodule = this.xmodule || false;
56104     }
56105     if (typeof(this.colModel.config) == 'undefined') {
56106         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56107         this.cm = this.colModel;
56108         this.cm.xmodule = this.xmodule || false;
56109     }
56110     if (this.dataSource) {
56111         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56112         this.ds = this.dataSource;
56113         this.ds.xmodule = this.xmodule || false;
56114          
56115     }
56116     
56117     
56118     
56119     if(this.width){
56120         this.container.setWidth(this.width);
56121     }
56122
56123     if(this.height){
56124         this.container.setHeight(this.height);
56125     }
56126     /** @private */
56127         this.addEvents({
56128         // raw events
56129         /**
56130          * @event click
56131          * The raw click event for the entire grid.
56132          * @param {Roo.EventObject} e
56133          */
56134         "click" : true,
56135         /**
56136          * @event dblclick
56137          * The raw dblclick event for the entire grid.
56138          * @param {Roo.EventObject} e
56139          */
56140         "dblclick" : true,
56141         /**
56142          * @event contextmenu
56143          * The raw contextmenu event for the entire grid.
56144          * @param {Roo.EventObject} e
56145          */
56146         "contextmenu" : true,
56147         /**
56148          * @event mousedown
56149          * The raw mousedown event for the entire grid.
56150          * @param {Roo.EventObject} e
56151          */
56152         "mousedown" : true,
56153         /**
56154          * @event mouseup
56155          * The raw mouseup event for the entire grid.
56156          * @param {Roo.EventObject} e
56157          */
56158         "mouseup" : true,
56159         /**
56160          * @event mouseover
56161          * The raw mouseover event for the entire grid.
56162          * @param {Roo.EventObject} e
56163          */
56164         "mouseover" : true,
56165         /**
56166          * @event mouseout
56167          * The raw mouseout event for the entire grid.
56168          * @param {Roo.EventObject} e
56169          */
56170         "mouseout" : true,
56171         /**
56172          * @event keypress
56173          * The raw keypress event for the entire grid.
56174          * @param {Roo.EventObject} e
56175          */
56176         "keypress" : true,
56177         /**
56178          * @event keydown
56179          * The raw keydown event for the entire grid.
56180          * @param {Roo.EventObject} e
56181          */
56182         "keydown" : true,
56183
56184         // custom events
56185
56186         /**
56187          * @event cellclick
56188          * Fires when a cell is clicked
56189          * @param {Grid} this
56190          * @param {Number} rowIndex
56191          * @param {Number} columnIndex
56192          * @param {Roo.EventObject} e
56193          */
56194         "cellclick" : true,
56195         /**
56196          * @event celldblclick
56197          * Fires when a cell is double clicked
56198          * @param {Grid} this
56199          * @param {Number} rowIndex
56200          * @param {Number} columnIndex
56201          * @param {Roo.EventObject} e
56202          */
56203         "celldblclick" : true,
56204         /**
56205          * @event rowclick
56206          * Fires when a row is clicked
56207          * @param {Grid} this
56208          * @param {Number} rowIndex
56209          * @param {Roo.EventObject} e
56210          */
56211         "rowclick" : true,
56212         /**
56213          * @event rowdblclick
56214          * Fires when a row is double clicked
56215          * @param {Grid} this
56216          * @param {Number} rowIndex
56217          * @param {Roo.EventObject} e
56218          */
56219         "rowdblclick" : true,
56220         /**
56221          * @event headerclick
56222          * Fires when a header is clicked
56223          * @param {Grid} this
56224          * @param {Number} columnIndex
56225          * @param {Roo.EventObject} e
56226          */
56227         "headerclick" : true,
56228         /**
56229          * @event headerdblclick
56230          * Fires when a header cell is double clicked
56231          * @param {Grid} this
56232          * @param {Number} columnIndex
56233          * @param {Roo.EventObject} e
56234          */
56235         "headerdblclick" : true,
56236         /**
56237          * @event rowcontextmenu
56238          * Fires when a row is right clicked
56239          * @param {Grid} this
56240          * @param {Number} rowIndex
56241          * @param {Roo.EventObject} e
56242          */
56243         "rowcontextmenu" : true,
56244         /**
56245          * @event cellcontextmenu
56246          * Fires when a cell is right clicked
56247          * @param {Grid} this
56248          * @param {Number} rowIndex
56249          * @param {Number} cellIndex
56250          * @param {Roo.EventObject} e
56251          */
56252          "cellcontextmenu" : true,
56253         /**
56254          * @event headercontextmenu
56255          * Fires when a header is right clicked
56256          * @param {Grid} this
56257          * @param {Number} columnIndex
56258          * @param {Roo.EventObject} e
56259          */
56260         "headercontextmenu" : true,
56261         /**
56262          * @event bodyscroll
56263          * Fires when the body element is scrolled
56264          * @param {Number} scrollLeft
56265          * @param {Number} scrollTop
56266          */
56267         "bodyscroll" : true,
56268         /**
56269          * @event columnresize
56270          * Fires when the user resizes a column
56271          * @param {Number} columnIndex
56272          * @param {Number} newSize
56273          */
56274         "columnresize" : true,
56275         /**
56276          * @event columnmove
56277          * Fires when the user moves a column
56278          * @param {Number} oldIndex
56279          * @param {Number} newIndex
56280          */
56281         "columnmove" : true,
56282         /**
56283          * @event startdrag
56284          * Fires when row(s) start being dragged
56285          * @param {Grid} this
56286          * @param {Roo.GridDD} dd The drag drop object
56287          * @param {event} e The raw browser event
56288          */
56289         "startdrag" : true,
56290         /**
56291          * @event enddrag
56292          * Fires when a drag operation is complete
56293          * @param {Grid} this
56294          * @param {Roo.GridDD} dd The drag drop object
56295          * @param {event} e The raw browser event
56296          */
56297         "enddrag" : true,
56298         /**
56299          * @event dragdrop
56300          * Fires when dragged row(s) are dropped on a valid DD target
56301          * @param {Grid} this
56302          * @param {Roo.GridDD} dd The drag drop object
56303          * @param {String} targetId The target drag drop object
56304          * @param {event} e The raw browser event
56305          */
56306         "dragdrop" : true,
56307         /**
56308          * @event dragover
56309          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56310          * @param {Grid} this
56311          * @param {Roo.GridDD} dd The drag drop object
56312          * @param {String} targetId The target drag drop object
56313          * @param {event} e The raw browser event
56314          */
56315         "dragover" : true,
56316         /**
56317          * @event dragenter
56318          *  Fires when the dragged row(s) first cross another DD target while being dragged
56319          * @param {Grid} this
56320          * @param {Roo.GridDD} dd The drag drop object
56321          * @param {String} targetId The target drag drop object
56322          * @param {event} e The raw browser event
56323          */
56324         "dragenter" : true,
56325         /**
56326          * @event dragout
56327          * Fires when the dragged row(s) leave another DD target while being dragged
56328          * @param {Grid} this
56329          * @param {Roo.GridDD} dd The drag drop object
56330          * @param {String} targetId The target drag drop object
56331          * @param {event} e The raw browser event
56332          */
56333         "dragout" : true,
56334         /**
56335          * @event rowclass
56336          * Fires when a row is rendered, so you can change add a style to it.
56337          * @param {GridView} gridview   The grid view
56338          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56339          */
56340         'rowclass' : true,
56341
56342         /**
56343          * @event render
56344          * Fires when the grid is rendered
56345          * @param {Grid} grid
56346          */
56347         'render' : true
56348     });
56349
56350     Roo.grid.Grid.superclass.constructor.call(this);
56351 };
56352 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56353     
56354     /**
56355          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56356          */
56357         /**
56358          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56359          */
56360         /**
56361          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56362          */
56363         /**
56364          * @cfg {Roo.grid.Store} ds The data store for the grid
56365          */
56366         /**
56367          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56368          */
56369         /**
56370      * @cfg {String} ddGroup - drag drop group.
56371      */
56372       /**
56373      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56374      */
56375
56376     /**
56377      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56378      */
56379     minColumnWidth : 25,
56380
56381     /**
56382      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56383      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56384      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56385      */
56386     autoSizeColumns : false,
56387
56388     /**
56389      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56390      */
56391     autoSizeHeaders : true,
56392
56393     /**
56394      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56395      */
56396     monitorWindowResize : true,
56397
56398     /**
56399      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56400      * rows measured to get a columns size. Default is 0 (all rows).
56401      */
56402     maxRowsToMeasure : 0,
56403
56404     /**
56405      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56406      */
56407     trackMouseOver : true,
56408
56409     /**
56410     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56411     */
56412       /**
56413     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56414     */
56415     
56416     /**
56417     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56418     */
56419     enableDragDrop : false,
56420     
56421     /**
56422     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56423     */
56424     enableColumnMove : true,
56425     
56426     /**
56427     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56428     */
56429     enableColumnHide : true,
56430     
56431     /**
56432     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56433     */
56434     enableRowHeightSync : false,
56435     
56436     /**
56437     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56438     */
56439     stripeRows : true,
56440     
56441     /**
56442     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
56443     */
56444     autoHeight : false,
56445
56446     /**
56447      * @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.
56448      */
56449     autoExpandColumn : false,
56450
56451     /**
56452     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
56453     * Default is 50.
56454     */
56455     autoExpandMin : 50,
56456
56457     /**
56458     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
56459     */
56460     autoExpandMax : 1000,
56461
56462     /**
56463     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
56464     */
56465     view : null,
56466
56467     /**
56468     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
56469     */
56470     loadMask : false,
56471     /**
56472     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
56473     */
56474     dropTarget: false,
56475     
56476    
56477     
56478     // private
56479     rendered : false,
56480
56481     /**
56482     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
56483     * of a fixed width. Default is false.
56484     */
56485     /**
56486     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
56487     */
56488     
56489     
56490     /**
56491     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
56492     * %0 is replaced with the number of selected rows.
56493     */
56494     ddText : "{0} selected row{1}",
56495     
56496     
56497     /**
56498      * Called once after all setup has been completed and the grid is ready to be rendered.
56499      * @return {Roo.grid.Grid} this
56500      */
56501     render : function()
56502     {
56503         var c = this.container;
56504         // try to detect autoHeight/width mode
56505         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
56506             this.autoHeight = true;
56507         }
56508         var view = this.getView();
56509         view.init(this);
56510
56511         c.on("click", this.onClick, this);
56512         c.on("dblclick", this.onDblClick, this);
56513         c.on("contextmenu", this.onContextMenu, this);
56514         c.on("keydown", this.onKeyDown, this);
56515         if (Roo.isTouch) {
56516             c.on("touchstart", this.onTouchStart, this);
56517         }
56518
56519         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
56520
56521         this.getSelectionModel().init(this);
56522
56523         view.render();
56524
56525         if(this.loadMask){
56526             this.loadMask = new Roo.LoadMask(this.container,
56527                     Roo.apply({store:this.dataSource}, this.loadMask));
56528         }
56529         
56530         
56531         if (this.toolbar && this.toolbar.xtype) {
56532             this.toolbar.container = this.getView().getHeaderPanel(true);
56533             this.toolbar = new Roo.Toolbar(this.toolbar);
56534         }
56535         if (this.footer && this.footer.xtype) {
56536             this.footer.dataSource = this.getDataSource();
56537             this.footer.container = this.getView().getFooterPanel(true);
56538             this.footer = Roo.factory(this.footer, Roo);
56539         }
56540         if (this.dropTarget && this.dropTarget.xtype) {
56541             delete this.dropTarget.xtype;
56542             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
56543         }
56544         
56545         
56546         this.rendered = true;
56547         this.fireEvent('render', this);
56548         return this;
56549     },
56550
56551     /**
56552      * Reconfigures the grid to use a different Store and Column Model.
56553      * The View will be bound to the new objects and refreshed.
56554      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
56555      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
56556      */
56557     reconfigure : function(dataSource, colModel){
56558         if(this.loadMask){
56559             this.loadMask.destroy();
56560             this.loadMask = new Roo.LoadMask(this.container,
56561                     Roo.apply({store:dataSource}, this.loadMask));
56562         }
56563         this.view.bind(dataSource, colModel);
56564         this.dataSource = dataSource;
56565         this.colModel = colModel;
56566         this.view.refresh(true);
56567     },
56568     /**
56569      * addColumns
56570      * Add's a column, default at the end..
56571      
56572      * @param {int} position to add (default end)
56573      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
56574      */
56575     addColumns : function(pos, ar)
56576     {
56577         
56578         for (var i =0;i< ar.length;i++) {
56579             var cfg = ar[i];
56580             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
56581             this.cm.lookup[cfg.id] = cfg;
56582         }
56583         
56584         
56585         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
56586             pos = this.cm.config.length; //this.cm.config.push(cfg);
56587         } 
56588         pos = Math.max(0,pos);
56589         ar.unshift(0);
56590         ar.unshift(pos);
56591         this.cm.config.splice.apply(this.cm.config, ar);
56592         
56593         
56594         
56595         this.view.generateRules(this.cm);
56596         this.view.refresh(true);
56597         
56598     },
56599     
56600     
56601     
56602     
56603     // private
56604     onKeyDown : function(e){
56605         this.fireEvent("keydown", e);
56606     },
56607
56608     /**
56609      * Destroy this grid.
56610      * @param {Boolean} removeEl True to remove the element
56611      */
56612     destroy : function(removeEl, keepListeners){
56613         if(this.loadMask){
56614             this.loadMask.destroy();
56615         }
56616         var c = this.container;
56617         c.removeAllListeners();
56618         this.view.destroy();
56619         this.colModel.purgeListeners();
56620         if(!keepListeners){
56621             this.purgeListeners();
56622         }
56623         c.update("");
56624         if(removeEl === true){
56625             c.remove();
56626         }
56627     },
56628
56629     // private
56630     processEvent : function(name, e){
56631         // does this fire select???
56632         //Roo.log('grid:processEvent '  + name);
56633         
56634         if (name != 'touchstart' ) {
56635             this.fireEvent(name, e);    
56636         }
56637         
56638         var t = e.getTarget();
56639         var v = this.view;
56640         var header = v.findHeaderIndex(t);
56641         if(header !== false){
56642             var ename = name == 'touchstart' ? 'click' : name;
56643              
56644             this.fireEvent("header" + ename, this, header, e);
56645         }else{
56646             var row = v.findRowIndex(t);
56647             var cell = v.findCellIndex(t);
56648             if (name == 'touchstart') {
56649                 // first touch is always a click.
56650                 // hopefull this happens after selection is updated.?
56651                 name = false;
56652                 
56653                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
56654                     var cs = this.selModel.getSelectedCell();
56655                     if (row == cs[0] && cell == cs[1]){
56656                         name = 'dblclick';
56657                     }
56658                 }
56659                 if (typeof(this.selModel.getSelections) != 'undefined') {
56660                     var cs = this.selModel.getSelections();
56661                     var ds = this.dataSource;
56662                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
56663                         name = 'dblclick';
56664                     }
56665                 }
56666                 if (!name) {
56667                     return;
56668                 }
56669             }
56670             
56671             
56672             if(row !== false){
56673                 this.fireEvent("row" + name, this, row, e);
56674                 if(cell !== false){
56675                     this.fireEvent("cell" + name, this, row, cell, e);
56676                 }
56677             }
56678         }
56679     },
56680
56681     // private
56682     onClick : function(e){
56683         this.processEvent("click", e);
56684     },
56685    // private
56686     onTouchStart : function(e){
56687         this.processEvent("touchstart", e);
56688     },
56689
56690     // private
56691     onContextMenu : function(e, t){
56692         this.processEvent("contextmenu", e);
56693     },
56694
56695     // private
56696     onDblClick : function(e){
56697         this.processEvent("dblclick", e);
56698     },
56699
56700     // private
56701     walkCells : function(row, col, step, fn, scope){
56702         var cm = this.colModel, clen = cm.getColumnCount();
56703         var ds = this.dataSource, rlen = ds.getCount(), first = true;
56704         if(step < 0){
56705             if(col < 0){
56706                 row--;
56707                 first = false;
56708             }
56709             while(row >= 0){
56710                 if(!first){
56711                     col = clen-1;
56712                 }
56713                 first = false;
56714                 while(col >= 0){
56715                     if(fn.call(scope || this, row, col, cm) === true){
56716                         return [row, col];
56717                     }
56718                     col--;
56719                 }
56720                 row--;
56721             }
56722         } else {
56723             if(col >= clen){
56724                 row++;
56725                 first = false;
56726             }
56727             while(row < rlen){
56728                 if(!first){
56729                     col = 0;
56730                 }
56731                 first = false;
56732                 while(col < clen){
56733                     if(fn.call(scope || this, row, col, cm) === true){
56734                         return [row, col];
56735                     }
56736                     col++;
56737                 }
56738                 row++;
56739             }
56740         }
56741         return null;
56742     },
56743
56744     // private
56745     getSelections : function(){
56746         return this.selModel.getSelections();
56747     },
56748
56749     /**
56750      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56751      * but if manual update is required this method will initiate it.
56752      */
56753     autoSize : function(){
56754         if(this.rendered){
56755             this.view.layout();
56756             if(this.view.adjustForScroll){
56757                 this.view.adjustForScroll();
56758             }
56759         }
56760     },
56761
56762     /**
56763      * Returns the grid's underlying element.
56764      * @return {Element} The element
56765      */
56766     getGridEl : function(){
56767         return this.container;
56768     },
56769
56770     // private for compatibility, overridden by editor grid
56771     stopEditing : function(){},
56772
56773     /**
56774      * Returns the grid's SelectionModel.
56775      * @return {SelectionModel}
56776      */
56777     getSelectionModel : function(){
56778         if(!this.selModel){
56779             this.selModel = new Roo.grid.RowSelectionModel();
56780         }
56781         return this.selModel;
56782     },
56783
56784     /**
56785      * Returns the grid's DataSource.
56786      * @return {DataSource}
56787      */
56788     getDataSource : function(){
56789         return this.dataSource;
56790     },
56791
56792     /**
56793      * Returns the grid's ColumnModel.
56794      * @return {ColumnModel}
56795      */
56796     getColumnModel : function(){
56797         return this.colModel;
56798     },
56799
56800     /**
56801      * Returns the grid's GridView object.
56802      * @return {GridView}
56803      */
56804     getView : function(){
56805         if(!this.view){
56806             this.view = new Roo.grid.GridView(this.viewConfig);
56807             this.relayEvents(this.view, [
56808                 "beforerowremoved", "beforerowsinserted",
56809                 "beforerefresh", "rowremoved",
56810                 "rowsinserted", "rowupdated" ,"refresh"
56811             ]);
56812         }
56813         return this.view;
56814     },
56815     /**
56816      * Called to get grid's drag proxy text, by default returns this.ddText.
56817      * Override this to put something different in the dragged text.
56818      * @return {String}
56819      */
56820     getDragDropText : function(){
56821         var count = this.selModel.getCount();
56822         return String.format(this.ddText, count, count == 1 ? '' : 's');
56823     }
56824 });
56825 /*
56826  * Based on:
56827  * Ext JS Library 1.1.1
56828  * Copyright(c) 2006-2007, Ext JS, LLC.
56829  *
56830  * Originally Released Under LGPL - original licence link has changed is not relivant.
56831  *
56832  * Fork - LGPL
56833  * <script type="text/javascript">
56834  */
56835  /**
56836  * @class Roo.grid.AbstractGridView
56837  * @extends Roo.util.Observable
56838  * @abstract
56839  * Abstract base class for grid Views
56840  * @constructor
56841  */
56842 Roo.grid.AbstractGridView = function(){
56843         this.grid = null;
56844         
56845         this.events = {
56846             "beforerowremoved" : true,
56847             "beforerowsinserted" : true,
56848             "beforerefresh" : true,
56849             "rowremoved" : true,
56850             "rowsinserted" : true,
56851             "rowupdated" : true,
56852             "refresh" : true
56853         };
56854     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56855 };
56856
56857 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56858     rowClass : "x-grid-row",
56859     cellClass : "x-grid-cell",
56860     tdClass : "x-grid-td",
56861     hdClass : "x-grid-hd",
56862     splitClass : "x-grid-hd-split",
56863     
56864     init: function(grid){
56865         this.grid = grid;
56866                 var cid = this.grid.getGridEl().id;
56867         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56868         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56869         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56870         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56871         },
56872         
56873     getColumnRenderers : function(){
56874         var renderers = [];
56875         var cm = this.grid.colModel;
56876         var colCount = cm.getColumnCount();
56877         for(var i = 0; i < colCount; i++){
56878             renderers[i] = cm.getRenderer(i);
56879         }
56880         return renderers;
56881     },
56882     
56883     getColumnIds : function(){
56884         var ids = [];
56885         var cm = this.grid.colModel;
56886         var colCount = cm.getColumnCount();
56887         for(var i = 0; i < colCount; i++){
56888             ids[i] = cm.getColumnId(i);
56889         }
56890         return ids;
56891     },
56892     
56893     getDataIndexes : function(){
56894         if(!this.indexMap){
56895             this.indexMap = this.buildIndexMap();
56896         }
56897         return this.indexMap.colToData;
56898     },
56899     
56900     getColumnIndexByDataIndex : function(dataIndex){
56901         if(!this.indexMap){
56902             this.indexMap = this.buildIndexMap();
56903         }
56904         return this.indexMap.dataToCol[dataIndex];
56905     },
56906     
56907     /**
56908      * Set a css style for a column dynamically. 
56909      * @param {Number} colIndex The index of the column
56910      * @param {String} name The css property name
56911      * @param {String} value The css value
56912      */
56913     setCSSStyle : function(colIndex, name, value){
56914         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56915         Roo.util.CSS.updateRule(selector, name, value);
56916     },
56917     
56918     generateRules : function(cm){
56919         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56920         Roo.util.CSS.removeStyleSheet(rulesId);
56921         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56922             var cid = cm.getColumnId(i);
56923             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56924                          this.tdSelector, cid, " {\n}\n",
56925                          this.hdSelector, cid, " {\n}\n",
56926                          this.splitSelector, cid, " {\n}\n");
56927         }
56928         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56929     }
56930 });/*
56931  * Based on:
56932  * Ext JS Library 1.1.1
56933  * Copyright(c) 2006-2007, Ext JS, LLC.
56934  *
56935  * Originally Released Under LGPL - original licence link has changed is not relivant.
56936  *
56937  * Fork - LGPL
56938  * <script type="text/javascript">
56939  */
56940
56941 // private
56942 // This is a support class used internally by the Grid components
56943 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56944     this.grid = grid;
56945     this.view = grid.getView();
56946     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56947     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56948     if(hd2){
56949         this.setHandleElId(Roo.id(hd));
56950         this.setOuterHandleElId(Roo.id(hd2));
56951     }
56952     this.scroll = false;
56953 };
56954 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56955     maxDragWidth: 120,
56956     getDragData : function(e){
56957         var t = Roo.lib.Event.getTarget(e);
56958         var h = this.view.findHeaderCell(t);
56959         if(h){
56960             return {ddel: h.firstChild, header:h};
56961         }
56962         return false;
56963     },
56964
56965     onInitDrag : function(e){
56966         this.view.headersDisabled = true;
56967         var clone = this.dragData.ddel.cloneNode(true);
56968         clone.id = Roo.id();
56969         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56970         this.proxy.update(clone);
56971         return true;
56972     },
56973
56974     afterValidDrop : function(){
56975         var v = this.view;
56976         setTimeout(function(){
56977             v.headersDisabled = false;
56978         }, 50);
56979     },
56980
56981     afterInvalidDrop : function(){
56982         var v = this.view;
56983         setTimeout(function(){
56984             v.headersDisabled = false;
56985         }, 50);
56986     }
56987 });
56988 /*
56989  * Based on:
56990  * Ext JS Library 1.1.1
56991  * Copyright(c) 2006-2007, Ext JS, LLC.
56992  *
56993  * Originally Released Under LGPL - original licence link has changed is not relivant.
56994  *
56995  * Fork - LGPL
56996  * <script type="text/javascript">
56997  */
56998 // private
56999 // This is a support class used internally by the Grid components
57000 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57001     this.grid = grid;
57002     this.view = grid.getView();
57003     // split the proxies so they don't interfere with mouse events
57004     this.proxyTop = Roo.DomHelper.append(document.body, {
57005         cls:"col-move-top", html:"&#160;"
57006     }, true);
57007     this.proxyBottom = Roo.DomHelper.append(document.body, {
57008         cls:"col-move-bottom", html:"&#160;"
57009     }, true);
57010     this.proxyTop.hide = this.proxyBottom.hide = function(){
57011         this.setLeftTop(-100,-100);
57012         this.setStyle("visibility", "hidden");
57013     };
57014     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57015     // temporarily disabled
57016     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57017     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57018 };
57019 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57020     proxyOffsets : [-4, -9],
57021     fly: Roo.Element.fly,
57022
57023     getTargetFromEvent : function(e){
57024         var t = Roo.lib.Event.getTarget(e);
57025         var cindex = this.view.findCellIndex(t);
57026         if(cindex !== false){
57027             return this.view.getHeaderCell(cindex);
57028         }
57029         return null;
57030     },
57031
57032     nextVisible : function(h){
57033         var v = this.view, cm = this.grid.colModel;
57034         h = h.nextSibling;
57035         while(h){
57036             if(!cm.isHidden(v.getCellIndex(h))){
57037                 return h;
57038             }
57039             h = h.nextSibling;
57040         }
57041         return null;
57042     },
57043
57044     prevVisible : function(h){
57045         var v = this.view, cm = this.grid.colModel;
57046         h = h.prevSibling;
57047         while(h){
57048             if(!cm.isHidden(v.getCellIndex(h))){
57049                 return h;
57050             }
57051             h = h.prevSibling;
57052         }
57053         return null;
57054     },
57055
57056     positionIndicator : function(h, n, e){
57057         var x = Roo.lib.Event.getPageX(e);
57058         var r = Roo.lib.Dom.getRegion(n.firstChild);
57059         var px, pt, py = r.top + this.proxyOffsets[1];
57060         if((r.right - x) <= (r.right-r.left)/2){
57061             px = r.right+this.view.borderWidth;
57062             pt = "after";
57063         }else{
57064             px = r.left;
57065             pt = "before";
57066         }
57067         var oldIndex = this.view.getCellIndex(h);
57068         var newIndex = this.view.getCellIndex(n);
57069
57070         if(this.grid.colModel.isFixed(newIndex)){
57071             return false;
57072         }
57073
57074         var locked = this.grid.colModel.isLocked(newIndex);
57075
57076         if(pt == "after"){
57077             newIndex++;
57078         }
57079         if(oldIndex < newIndex){
57080             newIndex--;
57081         }
57082         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57083             return false;
57084         }
57085         px +=  this.proxyOffsets[0];
57086         this.proxyTop.setLeftTop(px, py);
57087         this.proxyTop.show();
57088         if(!this.bottomOffset){
57089             this.bottomOffset = this.view.mainHd.getHeight();
57090         }
57091         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57092         this.proxyBottom.show();
57093         return pt;
57094     },
57095
57096     onNodeEnter : function(n, dd, e, data){
57097         if(data.header != n){
57098             this.positionIndicator(data.header, n, e);
57099         }
57100     },
57101
57102     onNodeOver : function(n, dd, e, data){
57103         var result = false;
57104         if(data.header != n){
57105             result = this.positionIndicator(data.header, n, e);
57106         }
57107         if(!result){
57108             this.proxyTop.hide();
57109             this.proxyBottom.hide();
57110         }
57111         return result ? this.dropAllowed : this.dropNotAllowed;
57112     },
57113
57114     onNodeOut : function(n, dd, e, data){
57115         this.proxyTop.hide();
57116         this.proxyBottom.hide();
57117     },
57118
57119     onNodeDrop : function(n, dd, e, data){
57120         var h = data.header;
57121         if(h != n){
57122             var cm = this.grid.colModel;
57123             var x = Roo.lib.Event.getPageX(e);
57124             var r = Roo.lib.Dom.getRegion(n.firstChild);
57125             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57126             var oldIndex = this.view.getCellIndex(h);
57127             var newIndex = this.view.getCellIndex(n);
57128             var locked = cm.isLocked(newIndex);
57129             if(pt == "after"){
57130                 newIndex++;
57131             }
57132             if(oldIndex < newIndex){
57133                 newIndex--;
57134             }
57135             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57136                 return false;
57137             }
57138             cm.setLocked(oldIndex, locked, true);
57139             cm.moveColumn(oldIndex, newIndex);
57140             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57141             return true;
57142         }
57143         return false;
57144     }
57145 });
57146 /*
57147  * Based on:
57148  * Ext JS Library 1.1.1
57149  * Copyright(c) 2006-2007, Ext JS, LLC.
57150  *
57151  * Originally Released Under LGPL - original licence link has changed is not relivant.
57152  *
57153  * Fork - LGPL
57154  * <script type="text/javascript">
57155  */
57156   
57157 /**
57158  * @class Roo.grid.GridView
57159  * @extends Roo.util.Observable
57160  *
57161  * @constructor
57162  * @param {Object} config
57163  */
57164 Roo.grid.GridView = function(config){
57165     Roo.grid.GridView.superclass.constructor.call(this);
57166     this.el = null;
57167
57168     Roo.apply(this, config);
57169 };
57170
57171 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57172
57173     unselectable :  'unselectable="on"',
57174     unselectableCls :  'x-unselectable',
57175     
57176     
57177     rowClass : "x-grid-row",
57178
57179     cellClass : "x-grid-col",
57180
57181     tdClass : "x-grid-td",
57182
57183     hdClass : "x-grid-hd",
57184
57185     splitClass : "x-grid-split",
57186
57187     sortClasses : ["sort-asc", "sort-desc"],
57188
57189     enableMoveAnim : false,
57190
57191     hlColor: "C3DAF9",
57192
57193     dh : Roo.DomHelper,
57194
57195     fly : Roo.Element.fly,
57196
57197     css : Roo.util.CSS,
57198
57199     borderWidth: 1,
57200
57201     splitOffset: 3,
57202
57203     scrollIncrement : 22,
57204
57205     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57206
57207     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57208
57209     bind : function(ds, cm){
57210         if(this.ds){
57211             this.ds.un("load", this.onLoad, this);
57212             this.ds.un("datachanged", this.onDataChange, this);
57213             this.ds.un("add", this.onAdd, this);
57214             this.ds.un("remove", this.onRemove, this);
57215             this.ds.un("update", this.onUpdate, this);
57216             this.ds.un("clear", this.onClear, this);
57217         }
57218         if(ds){
57219             ds.on("load", this.onLoad, this);
57220             ds.on("datachanged", this.onDataChange, this);
57221             ds.on("add", this.onAdd, this);
57222             ds.on("remove", this.onRemove, this);
57223             ds.on("update", this.onUpdate, this);
57224             ds.on("clear", this.onClear, this);
57225         }
57226         this.ds = ds;
57227
57228         if(this.cm){
57229             this.cm.un("widthchange", this.onColWidthChange, this);
57230             this.cm.un("headerchange", this.onHeaderChange, this);
57231             this.cm.un("hiddenchange", this.onHiddenChange, this);
57232             this.cm.un("columnmoved", this.onColumnMove, this);
57233             this.cm.un("columnlockchange", this.onColumnLock, this);
57234         }
57235         if(cm){
57236             this.generateRules(cm);
57237             cm.on("widthchange", this.onColWidthChange, this);
57238             cm.on("headerchange", this.onHeaderChange, this);
57239             cm.on("hiddenchange", this.onHiddenChange, this);
57240             cm.on("columnmoved", this.onColumnMove, this);
57241             cm.on("columnlockchange", this.onColumnLock, this);
57242         }
57243         this.cm = cm;
57244     },
57245
57246     init: function(grid){
57247         Roo.grid.GridView.superclass.init.call(this, grid);
57248
57249         this.bind(grid.dataSource, grid.colModel);
57250
57251         grid.on("headerclick", this.handleHeaderClick, this);
57252
57253         if(grid.trackMouseOver){
57254             grid.on("mouseover", this.onRowOver, this);
57255             grid.on("mouseout", this.onRowOut, this);
57256         }
57257         grid.cancelTextSelection = function(){};
57258         this.gridId = grid.id;
57259
57260         var tpls = this.templates || {};
57261
57262         if(!tpls.master){
57263             tpls.master = new Roo.Template(
57264                '<div class="x-grid" hidefocus="true">',
57265                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57266                   '<div class="x-grid-topbar"></div>',
57267                   '<div class="x-grid-scroller"><div></div></div>',
57268                   '<div class="x-grid-locked">',
57269                       '<div class="x-grid-header">{lockedHeader}</div>',
57270                       '<div class="x-grid-body">{lockedBody}</div>',
57271                   "</div>",
57272                   '<div class="x-grid-viewport">',
57273                       '<div class="x-grid-header">{header}</div>',
57274                       '<div class="x-grid-body">{body}</div>',
57275                   "</div>",
57276                   '<div class="x-grid-bottombar"></div>',
57277                  
57278                   '<div class="x-grid-resize-proxy">&#160;</div>',
57279                "</div>"
57280             );
57281             tpls.master.disableformats = true;
57282         }
57283
57284         if(!tpls.header){
57285             tpls.header = new Roo.Template(
57286                '<table border="0" cellspacing="0" cellpadding="0">',
57287                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57288                "</table>{splits}"
57289             );
57290             tpls.header.disableformats = true;
57291         }
57292         tpls.header.compile();
57293
57294         if(!tpls.hcell){
57295             tpls.hcell = new Roo.Template(
57296                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57297                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57298                 "</div></td>"
57299              );
57300              tpls.hcell.disableFormats = true;
57301         }
57302         tpls.hcell.compile();
57303
57304         if(!tpls.hsplit){
57305             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57306                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57307             tpls.hsplit.disableFormats = true;
57308         }
57309         tpls.hsplit.compile();
57310
57311         if(!tpls.body){
57312             tpls.body = new Roo.Template(
57313                '<table border="0" cellspacing="0" cellpadding="0">',
57314                "<tbody>{rows}</tbody>",
57315                "</table>"
57316             );
57317             tpls.body.disableFormats = true;
57318         }
57319         tpls.body.compile();
57320
57321         if(!tpls.row){
57322             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57323             tpls.row.disableFormats = true;
57324         }
57325         tpls.row.compile();
57326
57327         if(!tpls.cell){
57328             tpls.cell = new Roo.Template(
57329                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57330                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57331                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57332                 "</td>"
57333             );
57334             tpls.cell.disableFormats = true;
57335         }
57336         tpls.cell.compile();
57337
57338         this.templates = tpls;
57339     },
57340
57341     // remap these for backwards compat
57342     onColWidthChange : function(){
57343         this.updateColumns.apply(this, arguments);
57344     },
57345     onHeaderChange : function(){
57346         this.updateHeaders.apply(this, arguments);
57347     }, 
57348     onHiddenChange : function(){
57349         this.handleHiddenChange.apply(this, arguments);
57350     },
57351     onColumnMove : function(){
57352         this.handleColumnMove.apply(this, arguments);
57353     },
57354     onColumnLock : function(){
57355         this.handleLockChange.apply(this, arguments);
57356     },
57357
57358     onDataChange : function(){
57359         this.refresh();
57360         this.updateHeaderSortState();
57361     },
57362
57363     onClear : function(){
57364         this.refresh();
57365     },
57366
57367     onUpdate : function(ds, record){
57368         this.refreshRow(record);
57369     },
57370
57371     refreshRow : function(record){
57372         var ds = this.ds, index;
57373         if(typeof record == 'number'){
57374             index = record;
57375             record = ds.getAt(index);
57376         }else{
57377             index = ds.indexOf(record);
57378         }
57379         this.insertRows(ds, index, index, true);
57380         this.onRemove(ds, record, index+1, true);
57381         this.syncRowHeights(index, index);
57382         this.layout();
57383         this.fireEvent("rowupdated", this, index, record);
57384     },
57385
57386     onAdd : function(ds, records, index){
57387         this.insertRows(ds, index, index + (records.length-1));
57388     },
57389
57390     onRemove : function(ds, record, index, isUpdate){
57391         if(isUpdate !== true){
57392             this.fireEvent("beforerowremoved", this, index, record);
57393         }
57394         var bt = this.getBodyTable(), lt = this.getLockedTable();
57395         if(bt.rows[index]){
57396             bt.firstChild.removeChild(bt.rows[index]);
57397         }
57398         if(lt.rows[index]){
57399             lt.firstChild.removeChild(lt.rows[index]);
57400         }
57401         if(isUpdate !== true){
57402             this.stripeRows(index);
57403             this.syncRowHeights(index, index);
57404             this.layout();
57405             this.fireEvent("rowremoved", this, index, record);
57406         }
57407     },
57408
57409     onLoad : function(){
57410         this.scrollToTop();
57411     },
57412
57413     /**
57414      * Scrolls the grid to the top
57415      */
57416     scrollToTop : function(){
57417         if(this.scroller){
57418             this.scroller.dom.scrollTop = 0;
57419             this.syncScroll();
57420         }
57421     },
57422
57423     /**
57424      * Gets a panel in the header of the grid that can be used for toolbars etc.
57425      * After modifying the contents of this panel a call to grid.autoSize() may be
57426      * required to register any changes in size.
57427      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57428      * @return Roo.Element
57429      */
57430     getHeaderPanel : function(doShow){
57431         if(doShow){
57432             this.headerPanel.show();
57433         }
57434         return this.headerPanel;
57435     },
57436
57437     /**
57438      * Gets a panel in the footer of the grid that can be used for toolbars etc.
57439      * After modifying the contents of this panel a call to grid.autoSize() may be
57440      * required to register any changes in size.
57441      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
57442      * @return Roo.Element
57443      */
57444     getFooterPanel : function(doShow){
57445         if(doShow){
57446             this.footerPanel.show();
57447         }
57448         return this.footerPanel;
57449     },
57450
57451     initElements : function(){
57452         var E = Roo.Element;
57453         var el = this.grid.getGridEl().dom.firstChild;
57454         var cs = el.childNodes;
57455
57456         this.el = new E(el);
57457         
57458          this.focusEl = new E(el.firstChild);
57459         this.focusEl.swallowEvent("click", true);
57460         
57461         this.headerPanel = new E(cs[1]);
57462         this.headerPanel.enableDisplayMode("block");
57463
57464         this.scroller = new E(cs[2]);
57465         this.scrollSizer = new E(this.scroller.dom.firstChild);
57466
57467         this.lockedWrap = new E(cs[3]);
57468         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
57469         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
57470
57471         this.mainWrap = new E(cs[4]);
57472         this.mainHd = new E(this.mainWrap.dom.firstChild);
57473         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
57474
57475         this.footerPanel = new E(cs[5]);
57476         this.footerPanel.enableDisplayMode("block");
57477
57478         this.resizeProxy = new E(cs[6]);
57479
57480         this.headerSelector = String.format(
57481            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
57482            this.lockedHd.id, this.mainHd.id
57483         );
57484
57485         this.splitterSelector = String.format(
57486            '#{0} div.x-grid-split, #{1} div.x-grid-split',
57487            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
57488         );
57489     },
57490     idToCssName : function(s)
57491     {
57492         return s.replace(/[^a-z0-9]+/ig, '-');
57493     },
57494
57495     getHeaderCell : function(index){
57496         return Roo.DomQuery.select(this.headerSelector)[index];
57497     },
57498
57499     getHeaderCellMeasure : function(index){
57500         return this.getHeaderCell(index).firstChild;
57501     },
57502
57503     getHeaderCellText : function(index){
57504         return this.getHeaderCell(index).firstChild.firstChild;
57505     },
57506
57507     getLockedTable : function(){
57508         return this.lockedBody.dom.firstChild;
57509     },
57510
57511     getBodyTable : function(){
57512         return this.mainBody.dom.firstChild;
57513     },
57514
57515     getLockedRow : function(index){
57516         return this.getLockedTable().rows[index];
57517     },
57518
57519     getRow : function(index){
57520         return this.getBodyTable().rows[index];
57521     },
57522
57523     getRowComposite : function(index){
57524         if(!this.rowEl){
57525             this.rowEl = new Roo.CompositeElementLite();
57526         }
57527         var els = [], lrow, mrow;
57528         if(lrow = this.getLockedRow(index)){
57529             els.push(lrow);
57530         }
57531         if(mrow = this.getRow(index)){
57532             els.push(mrow);
57533         }
57534         this.rowEl.elements = els;
57535         return this.rowEl;
57536     },
57537     /**
57538      * Gets the 'td' of the cell
57539      * 
57540      * @param {Integer} rowIndex row to select
57541      * @param {Integer} colIndex column to select
57542      * 
57543      * @return {Object} 
57544      */
57545     getCell : function(rowIndex, colIndex){
57546         var locked = this.cm.getLockedCount();
57547         var source;
57548         if(colIndex < locked){
57549             source = this.lockedBody.dom.firstChild;
57550         }else{
57551             source = this.mainBody.dom.firstChild;
57552             colIndex -= locked;
57553         }
57554         return source.rows[rowIndex].childNodes[colIndex];
57555     },
57556
57557     getCellText : function(rowIndex, colIndex){
57558         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
57559     },
57560
57561     getCellBox : function(cell){
57562         var b = this.fly(cell).getBox();
57563         if(Roo.isOpera){ // opera fails to report the Y
57564             b.y = cell.offsetTop + this.mainBody.getY();
57565         }
57566         return b;
57567     },
57568
57569     getCellIndex : function(cell){
57570         var id = String(cell.className).match(this.cellRE);
57571         if(id){
57572             return parseInt(id[1], 10);
57573         }
57574         return 0;
57575     },
57576
57577     findHeaderIndex : function(n){
57578         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57579         return r ? this.getCellIndex(r) : false;
57580     },
57581
57582     findHeaderCell : function(n){
57583         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57584         return r ? r : false;
57585     },
57586
57587     findRowIndex : function(n){
57588         if(!n){
57589             return false;
57590         }
57591         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
57592         return r ? r.rowIndex : false;
57593     },
57594
57595     findCellIndex : function(node){
57596         var stop = this.el.dom;
57597         while(node && node != stop){
57598             if(this.findRE.test(node.className)){
57599                 return this.getCellIndex(node);
57600             }
57601             node = node.parentNode;
57602         }
57603         return false;
57604     },
57605
57606     getColumnId : function(index){
57607         return this.cm.getColumnId(index);
57608     },
57609
57610     getSplitters : function()
57611     {
57612         if(this.splitterSelector){
57613            return Roo.DomQuery.select(this.splitterSelector);
57614         }else{
57615             return null;
57616       }
57617     },
57618
57619     getSplitter : function(index){
57620         return this.getSplitters()[index];
57621     },
57622
57623     onRowOver : function(e, t){
57624         var row;
57625         if((row = this.findRowIndex(t)) !== false){
57626             this.getRowComposite(row).addClass("x-grid-row-over");
57627         }
57628     },
57629
57630     onRowOut : function(e, t){
57631         var row;
57632         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
57633             this.getRowComposite(row).removeClass("x-grid-row-over");
57634         }
57635     },
57636
57637     renderHeaders : function(){
57638         var cm = this.cm;
57639         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
57640         var cb = [], lb = [], sb = [], lsb = [], p = {};
57641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57642             p.cellId = "x-grid-hd-0-" + i;
57643             p.splitId = "x-grid-csplit-0-" + i;
57644             p.id = cm.getColumnId(i);
57645             p.value = cm.getColumnHeader(i) || "";
57646             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
57647             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
57648             if(!cm.isLocked(i)){
57649                 cb[cb.length] = ct.apply(p);
57650                 sb[sb.length] = st.apply(p);
57651             }else{
57652                 lb[lb.length] = ct.apply(p);
57653                 lsb[lsb.length] = st.apply(p);
57654             }
57655         }
57656         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
57657                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
57658     },
57659
57660     updateHeaders : function(){
57661         var html = this.renderHeaders();
57662         this.lockedHd.update(html[0]);
57663         this.mainHd.update(html[1]);
57664     },
57665
57666     /**
57667      * Focuses the specified row.
57668      * @param {Number} row The row index
57669      */
57670     focusRow : function(row)
57671     {
57672         //Roo.log('GridView.focusRow');
57673         var x = this.scroller.dom.scrollLeft;
57674         this.focusCell(row, 0, false);
57675         this.scroller.dom.scrollLeft = x;
57676     },
57677
57678     /**
57679      * Focuses the specified cell.
57680      * @param {Number} row The row index
57681      * @param {Number} col The column index
57682      * @param {Boolean} hscroll false to disable horizontal scrolling
57683      */
57684     focusCell : function(row, col, hscroll)
57685     {
57686         //Roo.log('GridView.focusCell');
57687         var el = this.ensureVisible(row, col, hscroll);
57688         this.focusEl.alignTo(el, "tl-tl");
57689         if(Roo.isGecko){
57690             this.focusEl.focus();
57691         }else{
57692             this.focusEl.focus.defer(1, this.focusEl);
57693         }
57694     },
57695
57696     /**
57697      * Scrolls the specified cell into view
57698      * @param {Number} row The row index
57699      * @param {Number} col The column index
57700      * @param {Boolean} hscroll false to disable horizontal scrolling
57701      */
57702     ensureVisible : function(row, col, hscroll)
57703     {
57704         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
57705         //return null; //disable for testing.
57706         if(typeof row != "number"){
57707             row = row.rowIndex;
57708         }
57709         if(row < 0 && row >= this.ds.getCount()){
57710             return  null;
57711         }
57712         col = (col !== undefined ? col : 0);
57713         var cm = this.grid.colModel;
57714         while(cm.isHidden(col)){
57715             col++;
57716         }
57717
57718         var el = this.getCell(row, col);
57719         if(!el){
57720             return null;
57721         }
57722         var c = this.scroller.dom;
57723
57724         var ctop = parseInt(el.offsetTop, 10);
57725         var cleft = parseInt(el.offsetLeft, 10);
57726         var cbot = ctop + el.offsetHeight;
57727         var cright = cleft + el.offsetWidth;
57728         
57729         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57730         var stop = parseInt(c.scrollTop, 10);
57731         var sleft = parseInt(c.scrollLeft, 10);
57732         var sbot = stop + ch;
57733         var sright = sleft + c.clientWidth;
57734         /*
57735         Roo.log('GridView.ensureVisible:' +
57736                 ' ctop:' + ctop +
57737                 ' c.clientHeight:' + c.clientHeight +
57738                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57739                 ' stop:' + stop +
57740                 ' cbot:' + cbot +
57741                 ' sbot:' + sbot +
57742                 ' ch:' + ch  
57743                 );
57744         */
57745         if(ctop < stop){
57746             c.scrollTop = ctop;
57747             //Roo.log("set scrolltop to ctop DISABLE?");
57748         }else if(cbot > sbot){
57749             //Roo.log("set scrolltop to cbot-ch");
57750             c.scrollTop = cbot-ch;
57751         }
57752         
57753         if(hscroll !== false){
57754             if(cleft < sleft){
57755                 c.scrollLeft = cleft;
57756             }else if(cright > sright){
57757                 c.scrollLeft = cright-c.clientWidth;
57758             }
57759         }
57760          
57761         return el;
57762     },
57763
57764     updateColumns : function(){
57765         this.grid.stopEditing();
57766         var cm = this.grid.colModel, colIds = this.getColumnIds();
57767         //var totalWidth = cm.getTotalWidth();
57768         var pos = 0;
57769         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57770             //if(cm.isHidden(i)) continue;
57771             var w = cm.getColumnWidth(i);
57772             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57773             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57774         }
57775         this.updateSplitters();
57776     },
57777
57778     generateRules : function(cm){
57779         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57780         Roo.util.CSS.removeStyleSheet(rulesId);
57781         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57782             var cid = cm.getColumnId(i);
57783             var align = '';
57784             if(cm.config[i].align){
57785                 align = 'text-align:'+cm.config[i].align+';';
57786             }
57787             var hidden = '';
57788             if(cm.isHidden(i)){
57789                 hidden = 'display:none;';
57790             }
57791             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57792             ruleBuf.push(
57793                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57794                     this.hdSelector, cid, " {\n", align, width, "}\n",
57795                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57796                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57797         }
57798         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57799     },
57800
57801     updateSplitters : function(){
57802         var cm = this.cm, s = this.getSplitters();
57803         if(s){ // splitters not created yet
57804             var pos = 0, locked = true;
57805             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57806                 if(cm.isHidden(i)) {
57807                     continue;
57808                 }
57809                 var w = cm.getColumnWidth(i); // make sure it's a number
57810                 if(!cm.isLocked(i) && locked){
57811                     pos = 0;
57812                     locked = false;
57813                 }
57814                 pos += w;
57815                 s[i].style.left = (pos-this.splitOffset) + "px";
57816             }
57817         }
57818     },
57819
57820     handleHiddenChange : function(colModel, colIndex, hidden){
57821         if(hidden){
57822             this.hideColumn(colIndex);
57823         }else{
57824             this.unhideColumn(colIndex);
57825         }
57826     },
57827
57828     hideColumn : function(colIndex){
57829         var cid = this.getColumnId(colIndex);
57830         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57831         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57832         if(Roo.isSafari){
57833             this.updateHeaders();
57834         }
57835         this.updateSplitters();
57836         this.layout();
57837     },
57838
57839     unhideColumn : function(colIndex){
57840         var cid = this.getColumnId(colIndex);
57841         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57842         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57843
57844         if(Roo.isSafari){
57845             this.updateHeaders();
57846         }
57847         this.updateSplitters();
57848         this.layout();
57849     },
57850
57851     insertRows : function(dm, firstRow, lastRow, isUpdate){
57852         if(firstRow == 0 && lastRow == dm.getCount()-1){
57853             this.refresh();
57854         }else{
57855             if(!isUpdate){
57856                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57857             }
57858             var s = this.getScrollState();
57859             var markup = this.renderRows(firstRow, lastRow);
57860             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57861             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57862             this.restoreScroll(s);
57863             if(!isUpdate){
57864                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57865                 this.syncRowHeights(firstRow, lastRow);
57866                 this.stripeRows(firstRow);
57867                 this.layout();
57868             }
57869         }
57870     },
57871
57872     bufferRows : function(markup, target, index){
57873         var before = null, trows = target.rows, tbody = target.tBodies[0];
57874         if(index < trows.length){
57875             before = trows[index];
57876         }
57877         var b = document.createElement("div");
57878         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57879         var rows = b.firstChild.rows;
57880         for(var i = 0, len = rows.length; i < len; i++){
57881             if(before){
57882                 tbody.insertBefore(rows[0], before);
57883             }else{
57884                 tbody.appendChild(rows[0]);
57885             }
57886         }
57887         b.innerHTML = "";
57888         b = null;
57889     },
57890
57891     deleteRows : function(dm, firstRow, lastRow){
57892         if(dm.getRowCount()<1){
57893             this.fireEvent("beforerefresh", this);
57894             this.mainBody.update("");
57895             this.lockedBody.update("");
57896             this.fireEvent("refresh", this);
57897         }else{
57898             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57899             var bt = this.getBodyTable();
57900             var tbody = bt.firstChild;
57901             var rows = bt.rows;
57902             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57903                 tbody.removeChild(rows[firstRow]);
57904             }
57905             this.stripeRows(firstRow);
57906             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57907         }
57908     },
57909
57910     updateRows : function(dataSource, firstRow, lastRow){
57911         var s = this.getScrollState();
57912         this.refresh();
57913         this.restoreScroll(s);
57914     },
57915
57916     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57917         if(!noRefresh){
57918            this.refresh();
57919         }
57920         this.updateHeaderSortState();
57921     },
57922
57923     getScrollState : function(){
57924         
57925         var sb = this.scroller.dom;
57926         return {left: sb.scrollLeft, top: sb.scrollTop};
57927     },
57928
57929     stripeRows : function(startRow){
57930         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57931             return;
57932         }
57933         startRow = startRow || 0;
57934         var rows = this.getBodyTable().rows;
57935         var lrows = this.getLockedTable().rows;
57936         var cls = ' x-grid-row-alt ';
57937         for(var i = startRow, len = rows.length; i < len; i++){
57938             var row = rows[i], lrow = lrows[i];
57939             var isAlt = ((i+1) % 2 == 0);
57940             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57941             if(isAlt == hasAlt){
57942                 continue;
57943             }
57944             if(isAlt){
57945                 row.className += " x-grid-row-alt";
57946             }else{
57947                 row.className = row.className.replace("x-grid-row-alt", "");
57948             }
57949             if(lrow){
57950                 lrow.className = row.className;
57951             }
57952         }
57953     },
57954
57955     restoreScroll : function(state){
57956         //Roo.log('GridView.restoreScroll');
57957         var sb = this.scroller.dom;
57958         sb.scrollLeft = state.left;
57959         sb.scrollTop = state.top;
57960         this.syncScroll();
57961     },
57962
57963     syncScroll : function(){
57964         //Roo.log('GridView.syncScroll');
57965         var sb = this.scroller.dom;
57966         var sh = this.mainHd.dom;
57967         var bs = this.mainBody.dom;
57968         var lv = this.lockedBody.dom;
57969         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57970         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57971     },
57972
57973     handleScroll : function(e){
57974         this.syncScroll();
57975         var sb = this.scroller.dom;
57976         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57977         e.stopEvent();
57978     },
57979
57980     handleWheel : function(e){
57981         var d = e.getWheelDelta();
57982         this.scroller.dom.scrollTop -= d*22;
57983         // set this here to prevent jumpy scrolling on large tables
57984         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57985         e.stopEvent();
57986     },
57987
57988     renderRows : function(startRow, endRow){
57989         // pull in all the crap needed to render rows
57990         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57991         var colCount = cm.getColumnCount();
57992
57993         if(ds.getCount() < 1){
57994             return ["", ""];
57995         }
57996
57997         // build a map for all the columns
57998         var cs = [];
57999         for(var i = 0; i < colCount; i++){
58000             var name = cm.getDataIndex(i);
58001             cs[i] = {
58002                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58003                 renderer : cm.getRenderer(i),
58004                 id : cm.getColumnId(i),
58005                 locked : cm.isLocked(i),
58006                 has_editor : cm.isCellEditable(i)
58007             };
58008         }
58009
58010         startRow = startRow || 0;
58011         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58012
58013         // records to render
58014         var rs = ds.getRange(startRow, endRow);
58015
58016         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58017     },
58018
58019     // As much as I hate to duplicate code, this was branched because FireFox really hates
58020     // [].join("") on strings. The performance difference was substantial enough to
58021     // branch this function
58022     doRender : Roo.isGecko ?
58023             function(cs, rs, ds, startRow, colCount, stripe){
58024                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58025                 // buffers
58026                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58027                 
58028                 var hasListener = this.grid.hasListener('rowclass');
58029                 var rowcfg = {};
58030                 for(var j = 0, len = rs.length; j < len; j++){
58031                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58032                     for(var i = 0; i < colCount; i++){
58033                         c = cs[i];
58034                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58035                         p.id = c.id;
58036                         p.css = p.attr = "";
58037                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58038                         if(p.value == undefined || p.value === "") {
58039                             p.value = "&#160;";
58040                         }
58041                         if(c.has_editor){
58042                             p.css += ' x-grid-editable-cell';
58043                         }
58044                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58045                             p.css +=  ' x-grid-dirty-cell';
58046                         }
58047                         var markup = ct.apply(p);
58048                         if(!c.locked){
58049                             cb+= markup;
58050                         }else{
58051                             lcb+= markup;
58052                         }
58053                     }
58054                     var alt = [];
58055                     if(stripe && ((rowIndex+1) % 2 == 0)){
58056                         alt.push("x-grid-row-alt")
58057                     }
58058                     if(r.dirty){
58059                         alt.push(  " x-grid-dirty-row");
58060                     }
58061                     rp.cells = lcb;
58062                     if(this.getRowClass){
58063                         alt.push(this.getRowClass(r, rowIndex));
58064                     }
58065                     if (hasListener) {
58066                         rowcfg = {
58067                              
58068                             record: r,
58069                             rowIndex : rowIndex,
58070                             rowClass : ''
58071                         };
58072                         this.grid.fireEvent('rowclass', this, rowcfg);
58073                         alt.push(rowcfg.rowClass);
58074                     }
58075                     rp.alt = alt.join(" ");
58076                     lbuf+= rt.apply(rp);
58077                     rp.cells = cb;
58078                     buf+=  rt.apply(rp);
58079                 }
58080                 return [lbuf, buf];
58081             } :
58082             function(cs, rs, ds, startRow, colCount, stripe){
58083                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58084                 // buffers
58085                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58086                 var hasListener = this.grid.hasListener('rowclass');
58087  
58088                 var rowcfg = {};
58089                 for(var j = 0, len = rs.length; j < len; j++){
58090                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58091                     for(var i = 0; i < colCount; i++){
58092                         c = cs[i];
58093                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58094                         p.id = c.id;
58095                         p.css = p.attr = "";
58096                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58097                         if(p.value == undefined || p.value === "") {
58098                             p.value = "&#160;";
58099                         }
58100                         //Roo.log(c);
58101                          if(c.has_editor){
58102                             p.css += ' x-grid-editable-cell';
58103                         }
58104                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58105                             p.css += ' x-grid-dirty-cell' 
58106                         }
58107                         
58108                         var markup = ct.apply(p);
58109                         if(!c.locked){
58110                             cb[cb.length] = markup;
58111                         }else{
58112                             lcb[lcb.length] = markup;
58113                         }
58114                     }
58115                     var alt = [];
58116                     if(stripe && ((rowIndex+1) % 2 == 0)){
58117                         alt.push( "x-grid-row-alt");
58118                     }
58119                     if(r.dirty){
58120                         alt.push(" x-grid-dirty-row");
58121                     }
58122                     rp.cells = lcb;
58123                     if(this.getRowClass){
58124                         alt.push( this.getRowClass(r, rowIndex));
58125                     }
58126                     if (hasListener) {
58127                         rowcfg = {
58128                              
58129                             record: r,
58130                             rowIndex : rowIndex,
58131                             rowClass : ''
58132                         };
58133                         this.grid.fireEvent('rowclass', this, rowcfg);
58134                         alt.push(rowcfg.rowClass);
58135                     }
58136                     
58137                     rp.alt = alt.join(" ");
58138                     rp.cells = lcb.join("");
58139                     lbuf[lbuf.length] = rt.apply(rp);
58140                     rp.cells = cb.join("");
58141                     buf[buf.length] =  rt.apply(rp);
58142                 }
58143                 return [lbuf.join(""), buf.join("")];
58144             },
58145
58146     renderBody : function(){
58147         var markup = this.renderRows();
58148         var bt = this.templates.body;
58149         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58150     },
58151
58152     /**
58153      * Refreshes the grid
58154      * @param {Boolean} headersToo
58155      */
58156     refresh : function(headersToo){
58157         this.fireEvent("beforerefresh", this);
58158         this.grid.stopEditing();
58159         var result = this.renderBody();
58160         this.lockedBody.update(result[0]);
58161         this.mainBody.update(result[1]);
58162         if(headersToo === true){
58163             this.updateHeaders();
58164             this.updateColumns();
58165             this.updateSplitters();
58166             this.updateHeaderSortState();
58167         }
58168         this.syncRowHeights();
58169         this.layout();
58170         this.fireEvent("refresh", this);
58171     },
58172
58173     handleColumnMove : function(cm, oldIndex, newIndex){
58174         this.indexMap = null;
58175         var s = this.getScrollState();
58176         this.refresh(true);
58177         this.restoreScroll(s);
58178         this.afterMove(newIndex);
58179     },
58180
58181     afterMove : function(colIndex){
58182         if(this.enableMoveAnim && Roo.enableFx){
58183             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58184         }
58185         // if multisort - fix sortOrder, and reload..
58186         if (this.grid.dataSource.multiSort) {
58187             // the we can call sort again..
58188             var dm = this.grid.dataSource;
58189             var cm = this.grid.colModel;
58190             var so = [];
58191             for(var i = 0; i < cm.config.length; i++ ) {
58192                 
58193                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58194                     continue; // dont' bother, it's not in sort list or being set.
58195                 }
58196                 
58197                 so.push(cm.config[i].dataIndex);
58198             };
58199             dm.sortOrder = so;
58200             dm.load(dm.lastOptions);
58201             
58202             
58203         }
58204         
58205     },
58206
58207     updateCell : function(dm, rowIndex, dataIndex){
58208         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58209         if(typeof colIndex == "undefined"){ // not present in grid
58210             return;
58211         }
58212         var cm = this.grid.colModel;
58213         var cell = this.getCell(rowIndex, colIndex);
58214         var cellText = this.getCellText(rowIndex, colIndex);
58215
58216         var p = {
58217             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58218             id : cm.getColumnId(colIndex),
58219             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58220         };
58221         var renderer = cm.getRenderer(colIndex);
58222         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58223         if(typeof val == "undefined" || val === "") {
58224             val = "&#160;";
58225         }
58226         cellText.innerHTML = val;
58227         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58228         this.syncRowHeights(rowIndex, rowIndex);
58229     },
58230
58231     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58232         var maxWidth = 0;
58233         if(this.grid.autoSizeHeaders){
58234             var h = this.getHeaderCellMeasure(colIndex);
58235             maxWidth = Math.max(maxWidth, h.scrollWidth);
58236         }
58237         var tb, index;
58238         if(this.cm.isLocked(colIndex)){
58239             tb = this.getLockedTable();
58240             index = colIndex;
58241         }else{
58242             tb = this.getBodyTable();
58243             index = colIndex - this.cm.getLockedCount();
58244         }
58245         if(tb && tb.rows){
58246             var rows = tb.rows;
58247             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58248             for(var i = 0; i < stopIndex; i++){
58249                 var cell = rows[i].childNodes[index].firstChild;
58250                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58251             }
58252         }
58253         return maxWidth + /*margin for error in IE*/ 5;
58254     },
58255     /**
58256      * Autofit a column to its content.
58257      * @param {Number} colIndex
58258      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58259      */
58260      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58261          if(this.cm.isHidden(colIndex)){
58262              return; // can't calc a hidden column
58263          }
58264         if(forceMinSize){
58265             var cid = this.cm.getColumnId(colIndex);
58266             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58267            if(this.grid.autoSizeHeaders){
58268                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58269            }
58270         }
58271         var newWidth = this.calcColumnWidth(colIndex);
58272         this.cm.setColumnWidth(colIndex,
58273             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58274         if(!suppressEvent){
58275             this.grid.fireEvent("columnresize", colIndex, newWidth);
58276         }
58277     },
58278
58279     /**
58280      * Autofits all columns to their content and then expands to fit any extra space in the grid
58281      */
58282      autoSizeColumns : function(){
58283         var cm = this.grid.colModel;
58284         var colCount = cm.getColumnCount();
58285         for(var i = 0; i < colCount; i++){
58286             this.autoSizeColumn(i, true, true);
58287         }
58288         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58289             this.fitColumns();
58290         }else{
58291             this.updateColumns();
58292             this.layout();
58293         }
58294     },
58295
58296     /**
58297      * Autofits all columns to the grid's width proportionate with their current size
58298      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58299      */
58300     fitColumns : function(reserveScrollSpace){
58301         var cm = this.grid.colModel;
58302         var colCount = cm.getColumnCount();
58303         var cols = [];
58304         var width = 0;
58305         var i, w;
58306         for (i = 0; i < colCount; i++){
58307             if(!cm.isHidden(i) && !cm.isFixed(i)){
58308                 w = cm.getColumnWidth(i);
58309                 cols.push(i);
58310                 cols.push(w);
58311                 width += w;
58312             }
58313         }
58314         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58315         if(reserveScrollSpace){
58316             avail -= 17;
58317         }
58318         var frac = (avail - cm.getTotalWidth())/width;
58319         while (cols.length){
58320             w = cols.pop();
58321             i = cols.pop();
58322             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58323         }
58324         this.updateColumns();
58325         this.layout();
58326     },
58327
58328     onRowSelect : function(rowIndex){
58329         var row = this.getRowComposite(rowIndex);
58330         row.addClass("x-grid-row-selected");
58331     },
58332
58333     onRowDeselect : function(rowIndex){
58334         var row = this.getRowComposite(rowIndex);
58335         row.removeClass("x-grid-row-selected");
58336     },
58337
58338     onCellSelect : function(row, col){
58339         var cell = this.getCell(row, col);
58340         if(cell){
58341             Roo.fly(cell).addClass("x-grid-cell-selected");
58342         }
58343     },
58344
58345     onCellDeselect : function(row, col){
58346         var cell = this.getCell(row, col);
58347         if(cell){
58348             Roo.fly(cell).removeClass("x-grid-cell-selected");
58349         }
58350     },
58351
58352     updateHeaderSortState : function(){
58353         
58354         // sort state can be single { field: xxx, direction : yyy}
58355         // or   { xxx=>ASC , yyy : DESC ..... }
58356         
58357         var mstate = {};
58358         if (!this.ds.multiSort) { 
58359             var state = this.ds.getSortState();
58360             if(!state){
58361                 return;
58362             }
58363             mstate[state.field] = state.direction;
58364             // FIXME... - this is not used here.. but might be elsewhere..
58365             this.sortState = state;
58366             
58367         } else {
58368             mstate = this.ds.sortToggle;
58369         }
58370         //remove existing sort classes..
58371         
58372         var sc = this.sortClasses;
58373         var hds = this.el.select(this.headerSelector).removeClass(sc);
58374         
58375         for(var f in mstate) {
58376         
58377             var sortColumn = this.cm.findColumnIndex(f);
58378             
58379             if(sortColumn != -1){
58380                 var sortDir = mstate[f];        
58381                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58382             }
58383         }
58384         
58385          
58386         
58387     },
58388
58389
58390     handleHeaderClick : function(g, index,e){
58391         
58392         Roo.log("header click");
58393         
58394         if (Roo.isTouch) {
58395             // touch events on header are handled by context
58396             this.handleHdCtx(g,index,e);
58397             return;
58398         }
58399         
58400         
58401         if(this.headersDisabled){
58402             return;
58403         }
58404         var dm = g.dataSource, cm = g.colModel;
58405         if(!cm.isSortable(index)){
58406             return;
58407         }
58408         g.stopEditing();
58409         
58410         if (dm.multiSort) {
58411             // update the sortOrder
58412             var so = [];
58413             for(var i = 0; i < cm.config.length; i++ ) {
58414                 
58415                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58416                     continue; // dont' bother, it's not in sort list or being set.
58417                 }
58418                 
58419                 so.push(cm.config[i].dataIndex);
58420             };
58421             dm.sortOrder = so;
58422         }
58423         
58424         
58425         dm.sort(cm.getDataIndex(index));
58426     },
58427
58428
58429     destroy : function(){
58430         if(this.colMenu){
58431             this.colMenu.removeAll();
58432             Roo.menu.MenuMgr.unregister(this.colMenu);
58433             this.colMenu.getEl().remove();
58434             delete this.colMenu;
58435         }
58436         if(this.hmenu){
58437             this.hmenu.removeAll();
58438             Roo.menu.MenuMgr.unregister(this.hmenu);
58439             this.hmenu.getEl().remove();
58440             delete this.hmenu;
58441         }
58442         if(this.grid.enableColumnMove){
58443             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58444             if(dds){
58445                 for(var dd in dds){
58446                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
58447                         var elid = dds[dd].dragElId;
58448                         dds[dd].unreg();
58449                         Roo.get(elid).remove();
58450                     } else if(dds[dd].config.isTarget){
58451                         dds[dd].proxyTop.remove();
58452                         dds[dd].proxyBottom.remove();
58453                         dds[dd].unreg();
58454                     }
58455                     if(Roo.dd.DDM.locationCache[dd]){
58456                         delete Roo.dd.DDM.locationCache[dd];
58457                     }
58458                 }
58459                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58460             }
58461         }
58462         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
58463         this.bind(null, null);
58464         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
58465     },
58466
58467     handleLockChange : function(){
58468         this.refresh(true);
58469     },
58470
58471     onDenyColumnLock : function(){
58472
58473     },
58474
58475     onDenyColumnHide : function(){
58476
58477     },
58478
58479     handleHdMenuClick : function(item){
58480         var index = this.hdCtxIndex;
58481         var cm = this.cm, ds = this.ds;
58482         switch(item.id){
58483             case "asc":
58484                 ds.sort(cm.getDataIndex(index), "ASC");
58485                 break;
58486             case "desc":
58487                 ds.sort(cm.getDataIndex(index), "DESC");
58488                 break;
58489             case "lock":
58490                 var lc = cm.getLockedCount();
58491                 if(cm.getColumnCount(true) <= lc+1){
58492                     this.onDenyColumnLock();
58493                     return;
58494                 }
58495                 if(lc != index){
58496                     cm.setLocked(index, true, true);
58497                     cm.moveColumn(index, lc);
58498                     this.grid.fireEvent("columnmove", index, lc);
58499                 }else{
58500                     cm.setLocked(index, true);
58501                 }
58502             break;
58503             case "unlock":
58504                 var lc = cm.getLockedCount();
58505                 if((lc-1) != index){
58506                     cm.setLocked(index, false, true);
58507                     cm.moveColumn(index, lc-1);
58508                     this.grid.fireEvent("columnmove", index, lc-1);
58509                 }else{
58510                     cm.setLocked(index, false);
58511                 }
58512             break;
58513             case 'wider': // used to expand cols on touch..
58514             case 'narrow':
58515                 var cw = cm.getColumnWidth(index);
58516                 cw += (item.id == 'wider' ? 1 : -1) * 50;
58517                 cw = Math.max(0, cw);
58518                 cw = Math.min(cw,4000);
58519                 cm.setColumnWidth(index, cw);
58520                 break;
58521                 
58522             default:
58523                 index = cm.getIndexById(item.id.substr(4));
58524                 if(index != -1){
58525                     if(item.checked && cm.getColumnCount(true) <= 1){
58526                         this.onDenyColumnHide();
58527                         return false;
58528                     }
58529                     cm.setHidden(index, item.checked);
58530                 }
58531         }
58532         return true;
58533     },
58534
58535     beforeColMenuShow : function(){
58536         var cm = this.cm,  colCount = cm.getColumnCount();
58537         this.colMenu.removeAll();
58538         for(var i = 0; i < colCount; i++){
58539             this.colMenu.add(new Roo.menu.CheckItem({
58540                 id: "col-"+cm.getColumnId(i),
58541                 text: cm.getColumnHeader(i),
58542                 checked: !cm.isHidden(i),
58543                 hideOnClick:false
58544             }));
58545         }
58546     },
58547
58548     handleHdCtx : function(g, index, e){
58549         e.stopEvent();
58550         var hd = this.getHeaderCell(index);
58551         this.hdCtxIndex = index;
58552         var ms = this.hmenu.items, cm = this.cm;
58553         ms.get("asc").setDisabled(!cm.isSortable(index));
58554         ms.get("desc").setDisabled(!cm.isSortable(index));
58555         if(this.grid.enableColLock !== false){
58556             ms.get("lock").setDisabled(cm.isLocked(index));
58557             ms.get("unlock").setDisabled(!cm.isLocked(index));
58558         }
58559         this.hmenu.show(hd, "tl-bl");
58560     },
58561
58562     handleHdOver : function(e){
58563         var hd = this.findHeaderCell(e.getTarget());
58564         if(hd && !this.headersDisabled){
58565             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
58566                this.fly(hd).addClass("x-grid-hd-over");
58567             }
58568         }
58569     },
58570
58571     handleHdOut : function(e){
58572         var hd = this.findHeaderCell(e.getTarget());
58573         if(hd){
58574             this.fly(hd).removeClass("x-grid-hd-over");
58575         }
58576     },
58577
58578     handleSplitDblClick : function(e, t){
58579         var i = this.getCellIndex(t);
58580         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
58581             this.autoSizeColumn(i, true);
58582             this.layout();
58583         }
58584     },
58585
58586     render : function(){
58587
58588         var cm = this.cm;
58589         var colCount = cm.getColumnCount();
58590
58591         if(this.grid.monitorWindowResize === true){
58592             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58593         }
58594         var header = this.renderHeaders();
58595         var body = this.templates.body.apply({rows:""});
58596         var html = this.templates.master.apply({
58597             lockedBody: body,
58598             body: body,
58599             lockedHeader: header[0],
58600             header: header[1]
58601         });
58602
58603         //this.updateColumns();
58604
58605         this.grid.getGridEl().dom.innerHTML = html;
58606
58607         this.initElements();
58608         
58609         // a kludge to fix the random scolling effect in webkit
58610         this.el.on("scroll", function() {
58611             this.el.dom.scrollTop=0; // hopefully not recursive..
58612         },this);
58613
58614         this.scroller.on("scroll", this.handleScroll, this);
58615         this.lockedBody.on("mousewheel", this.handleWheel, this);
58616         this.mainBody.on("mousewheel", this.handleWheel, this);
58617
58618         this.mainHd.on("mouseover", this.handleHdOver, this);
58619         this.mainHd.on("mouseout", this.handleHdOut, this);
58620         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
58621                 {delegate: "."+this.splitClass});
58622
58623         this.lockedHd.on("mouseover", this.handleHdOver, this);
58624         this.lockedHd.on("mouseout", this.handleHdOut, this);
58625         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
58626                 {delegate: "."+this.splitClass});
58627
58628         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
58629             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58630         }
58631
58632         this.updateSplitters();
58633
58634         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
58635             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58636             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58637         }
58638
58639         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
58640             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
58641             this.hmenu.add(
58642                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
58643                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
58644             );
58645             if(this.grid.enableColLock !== false){
58646                 this.hmenu.add('-',
58647                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
58648                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
58649                 );
58650             }
58651             if (Roo.isTouch) {
58652                  this.hmenu.add('-',
58653                     {id:"wider", text: this.columnsWiderText},
58654                     {id:"narrow", text: this.columnsNarrowText }
58655                 );
58656                 
58657                  
58658             }
58659             
58660             if(this.grid.enableColumnHide !== false){
58661
58662                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
58663                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
58664                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
58665
58666                 this.hmenu.add('-',
58667                     {id:"columns", text: this.columnsText, menu: this.colMenu}
58668                 );
58669             }
58670             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
58671
58672             this.grid.on("headercontextmenu", this.handleHdCtx, this);
58673         }
58674
58675         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
58676             this.dd = new Roo.grid.GridDragZone(this.grid, {
58677                 ddGroup : this.grid.ddGroup || 'GridDD'
58678             });
58679             
58680         }
58681
58682         /*
58683         for(var i = 0; i < colCount; i++){
58684             if(cm.isHidden(i)){
58685                 this.hideColumn(i);
58686             }
58687             if(cm.config[i].align){
58688                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
58689                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
58690             }
58691         }*/
58692         
58693         this.updateHeaderSortState();
58694
58695         this.beforeInitialResize();
58696         this.layout(true);
58697
58698         // two part rendering gives faster view to the user
58699         this.renderPhase2.defer(1, this);
58700     },
58701
58702     renderPhase2 : function(){
58703         // render the rows now
58704         this.refresh();
58705         if(this.grid.autoSizeColumns){
58706             this.autoSizeColumns();
58707         }
58708     },
58709
58710     beforeInitialResize : function(){
58711
58712     },
58713
58714     onColumnSplitterMoved : function(i, w){
58715         this.userResized = true;
58716         var cm = this.grid.colModel;
58717         cm.setColumnWidth(i, w, true);
58718         var cid = cm.getColumnId(i);
58719         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58720         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58721         this.updateSplitters();
58722         this.layout();
58723         this.grid.fireEvent("columnresize", i, w);
58724     },
58725
58726     syncRowHeights : function(startIndex, endIndex){
58727         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58728             startIndex = startIndex || 0;
58729             var mrows = this.getBodyTable().rows;
58730             var lrows = this.getLockedTable().rows;
58731             var len = mrows.length-1;
58732             endIndex = Math.min(endIndex || len, len);
58733             for(var i = startIndex; i <= endIndex; i++){
58734                 var m = mrows[i], l = lrows[i];
58735                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58736                 m.style.height = l.style.height = h + "px";
58737             }
58738         }
58739     },
58740
58741     layout : function(initialRender, is2ndPass)
58742     {
58743         var g = this.grid;
58744         var auto = g.autoHeight;
58745         var scrollOffset = 16;
58746         var c = g.getGridEl(), cm = this.cm,
58747                 expandCol = g.autoExpandColumn,
58748                 gv = this;
58749         //c.beginMeasure();
58750
58751         if(!c.dom.offsetWidth){ // display:none?
58752             if(initialRender){
58753                 this.lockedWrap.show();
58754                 this.mainWrap.show();
58755             }
58756             return;
58757         }
58758
58759         var hasLock = this.cm.isLocked(0);
58760
58761         var tbh = this.headerPanel.getHeight();
58762         var bbh = this.footerPanel.getHeight();
58763
58764         if(auto){
58765             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58766             var newHeight = ch + c.getBorderWidth("tb");
58767             if(g.maxHeight){
58768                 newHeight = Math.min(g.maxHeight, newHeight);
58769             }
58770             c.setHeight(newHeight);
58771         }
58772
58773         if(g.autoWidth){
58774             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58775         }
58776
58777         var s = this.scroller;
58778
58779         var csize = c.getSize(true);
58780
58781         this.el.setSize(csize.width, csize.height);
58782
58783         this.headerPanel.setWidth(csize.width);
58784         this.footerPanel.setWidth(csize.width);
58785
58786         var hdHeight = this.mainHd.getHeight();
58787         var vw = csize.width;
58788         var vh = csize.height - (tbh + bbh);
58789
58790         s.setSize(vw, vh);
58791
58792         var bt = this.getBodyTable();
58793         
58794         if(cm.getLockedCount() == cm.config.length){
58795             bt = this.getLockedTable();
58796         }
58797         
58798         var ltWidth = hasLock ?
58799                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
58800
58801         var scrollHeight = bt.offsetHeight;
58802         var scrollWidth = ltWidth + bt.offsetWidth;
58803         var vscroll = false, hscroll = false;
58804
58805         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
58806
58807         var lw = this.lockedWrap, mw = this.mainWrap;
58808         var lb = this.lockedBody, mb = this.mainBody;
58809
58810         setTimeout(function(){
58811             var t = s.dom.offsetTop;
58812             var w = s.dom.clientWidth,
58813                 h = s.dom.clientHeight;
58814
58815             lw.setTop(t);
58816             lw.setSize(ltWidth, h);
58817
58818             mw.setLeftTop(ltWidth, t);
58819             mw.setSize(w-ltWidth, h);
58820
58821             lb.setHeight(h-hdHeight);
58822             mb.setHeight(h-hdHeight);
58823
58824             if(is2ndPass !== true && !gv.userResized && expandCol){
58825                 // high speed resize without full column calculation
58826                 
58827                 var ci = cm.getIndexById(expandCol);
58828                 if (ci < 0) {
58829                     ci = cm.findColumnIndex(expandCol);
58830                 }
58831                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58832                 var expandId = cm.getColumnId(ci);
58833                 var  tw = cm.getTotalWidth(false);
58834                 var currentWidth = cm.getColumnWidth(ci);
58835                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58836                 if(currentWidth != cw){
58837                     cm.setColumnWidth(ci, cw, true);
58838                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58839                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58840                     gv.updateSplitters();
58841                     gv.layout(false, true);
58842                 }
58843             }
58844
58845             if(initialRender){
58846                 lw.show();
58847                 mw.show();
58848             }
58849             //c.endMeasure();
58850         }, 10);
58851     },
58852
58853     onWindowResize : function(){
58854         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58855             return;
58856         }
58857         this.layout();
58858     },
58859
58860     appendFooter : function(parentEl){
58861         return null;
58862     },
58863
58864     sortAscText : "Sort Ascending",
58865     sortDescText : "Sort Descending",
58866     lockText : "Lock Column",
58867     unlockText : "Unlock Column",
58868     columnsText : "Columns",
58869  
58870     columnsWiderText : "Wider",
58871     columnsNarrowText : "Thinner"
58872 });
58873
58874
58875 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58876     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58877     this.proxy.el.addClass('x-grid3-col-dd');
58878 };
58879
58880 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58881     handleMouseDown : function(e){
58882
58883     },
58884
58885     callHandleMouseDown : function(e){
58886         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58887     }
58888 });
58889 /*
58890  * Based on:
58891  * Ext JS Library 1.1.1
58892  * Copyright(c) 2006-2007, Ext JS, LLC.
58893  *
58894  * Originally Released Under LGPL - original licence link has changed is not relivant.
58895  *
58896  * Fork - LGPL
58897  * <script type="text/javascript">
58898  */
58899  /**
58900  * @extends Roo.dd.DDProxy
58901  * @class Roo.grid.SplitDragZone
58902  * Support for Column Header resizing
58903  * @constructor
58904  * @param {Object} config
58905  */
58906 // private
58907 // This is a support class used internally by the Grid components
58908 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58909     this.grid = grid;
58910     this.view = grid.getView();
58911     this.proxy = this.view.resizeProxy;
58912     Roo.grid.SplitDragZone.superclass.constructor.call(
58913         this,
58914         hd, // ID
58915         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58916         {  // CONFIG
58917             dragElId : Roo.id(this.proxy.dom),
58918             resizeFrame:false
58919         }
58920     );
58921     
58922     this.setHandleElId(Roo.id(hd));
58923     if (hd2 !== false) {
58924         this.setOuterHandleElId(Roo.id(hd2));
58925     }
58926     
58927     this.scroll = false;
58928 };
58929 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58930     fly: Roo.Element.fly,
58931
58932     b4StartDrag : function(x, y){
58933         this.view.headersDisabled = true;
58934         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58935                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58936         );
58937         this.proxy.setHeight(h);
58938         
58939         // for old system colWidth really stored the actual width?
58940         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58941         // which in reality did not work.. - it worked only for fixed sizes
58942         // for resizable we need to use actual sizes.
58943         var w = this.cm.getColumnWidth(this.cellIndex);
58944         if (!this.view.mainWrap) {
58945             // bootstrap.
58946             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58947         }
58948         
58949         
58950         
58951         // this was w-this.grid.minColumnWidth;
58952         // doesnt really make sense? - w = thie curren width or the rendered one?
58953         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58954         this.resetConstraints();
58955         this.setXConstraint(minw, 1000);
58956         this.setYConstraint(0, 0);
58957         this.minX = x - minw;
58958         this.maxX = x + 1000;
58959         this.startPos = x;
58960         if (!this.view.mainWrap) { // this is Bootstrap code..
58961             this.getDragEl().style.display='block';
58962         }
58963         
58964         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58965     },
58966
58967
58968     handleMouseDown : function(e){
58969         ev = Roo.EventObject.setEvent(e);
58970         var t = this.fly(ev.getTarget());
58971         if(t.hasClass("x-grid-split")){
58972             this.cellIndex = this.view.getCellIndex(t.dom);
58973             this.split = t.dom;
58974             this.cm = this.grid.colModel;
58975             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58976                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58977             }
58978         }
58979     },
58980
58981     endDrag : function(e){
58982         this.view.headersDisabled = false;
58983         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58984         var diff = endX - this.startPos;
58985         // 
58986         var w = this.cm.getColumnWidth(this.cellIndex);
58987         if (!this.view.mainWrap) {
58988             w = 0;
58989         }
58990         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58991     },
58992
58993     autoOffset : function(){
58994         this.setDelta(0,0);
58995     }
58996 });/*
58997  * Based on:
58998  * Ext JS Library 1.1.1
58999  * Copyright(c) 2006-2007, Ext JS, LLC.
59000  *
59001  * Originally Released Under LGPL - original licence link has changed is not relivant.
59002  *
59003  * Fork - LGPL
59004  * <script type="text/javascript">
59005  */
59006  
59007 // private
59008 // This is a support class used internally by the Grid components
59009 Roo.grid.GridDragZone = function(grid, config){
59010     this.view = grid.getView();
59011     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59012     if(this.view.lockedBody){
59013         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59014         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59015     }
59016     this.scroll = false;
59017     this.grid = grid;
59018     this.ddel = document.createElement('div');
59019     this.ddel.className = 'x-grid-dd-wrap';
59020 };
59021
59022 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59023     ddGroup : "GridDD",
59024
59025     getDragData : function(e){
59026         var t = Roo.lib.Event.getTarget(e);
59027         var rowIndex = this.view.findRowIndex(t);
59028         var sm = this.grid.selModel;
59029             
59030         //Roo.log(rowIndex);
59031         
59032         if (sm.getSelectedCell) {
59033             // cell selection..
59034             if (!sm.getSelectedCell()) {
59035                 return false;
59036             }
59037             if (rowIndex != sm.getSelectedCell()[0]) {
59038                 return false;
59039             }
59040         
59041         }
59042         if (sm.getSelections && sm.getSelections().length < 1) {
59043             return false;
59044         }
59045         
59046         
59047         // before it used to all dragging of unseleted... - now we dont do that.
59048         if(rowIndex !== false){
59049             
59050             // if editorgrid.. 
59051             
59052             
59053             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59054                
59055             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59056               //  
59057             //}
59058             if (e.hasModifier()){
59059                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59060             }
59061             
59062             Roo.log("getDragData");
59063             
59064             return {
59065                 grid: this.grid,
59066                 ddel: this.ddel,
59067                 rowIndex: rowIndex,
59068                 selections: sm.getSelections ? sm.getSelections() : (
59069                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59070             };
59071         }
59072         return false;
59073     },
59074     
59075     
59076     onInitDrag : function(e){
59077         var data = this.dragData;
59078         this.ddel.innerHTML = this.grid.getDragDropText();
59079         this.proxy.update(this.ddel);
59080         // fire start drag?
59081     },
59082
59083     afterRepair : function(){
59084         this.dragging = false;
59085     },
59086
59087     getRepairXY : function(e, data){
59088         return false;
59089     },
59090
59091     onEndDrag : function(data, e){
59092         // fire end drag?
59093     },
59094
59095     onValidDrop : function(dd, e, id){
59096         // fire drag drop?
59097         this.hideProxy();
59098     },
59099
59100     beforeInvalidDrop : function(e, id){
59101
59102     }
59103 });/*
59104  * Based on:
59105  * Ext JS Library 1.1.1
59106  * Copyright(c) 2006-2007, Ext JS, LLC.
59107  *
59108  * Originally Released Under LGPL - original licence link has changed is not relivant.
59109  *
59110  * Fork - LGPL
59111  * <script type="text/javascript">
59112  */
59113  
59114
59115 /**
59116  * @class Roo.grid.ColumnModel
59117  * @extends Roo.util.Observable
59118  * This is the default implementation of a ColumnModel used by the Grid. It defines
59119  * the columns in the grid.
59120  * <br>Usage:<br>
59121  <pre><code>
59122  var colModel = new Roo.grid.ColumnModel([
59123         {header: "Ticker", width: 60, sortable: true, locked: true},
59124         {header: "Company Name", width: 150, sortable: true},
59125         {header: "Market Cap.", width: 100, sortable: true},
59126         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59127         {header: "Employees", width: 100, sortable: true, resizable: false}
59128  ]);
59129  </code></pre>
59130  * <p>
59131  
59132  * The config options listed for this class are options which may appear in each
59133  * individual column definition.
59134  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59135  * @constructor
59136  * @param {Object} config An Array of column config objects. See this class's
59137  * config objects for details.
59138 */
59139 Roo.grid.ColumnModel = function(config){
59140         /**
59141      * The config passed into the constructor
59142      */
59143     this.config = []; //config;
59144     this.lookup = {};
59145
59146     // if no id, create one
59147     // if the column does not have a dataIndex mapping,
59148     // map it to the order it is in the config
59149     for(var i = 0, len = config.length; i < len; i++){
59150         this.addColumn(config[i]);
59151         
59152     }
59153
59154     /**
59155      * The width of columns which have no width specified (defaults to 100)
59156      * @type Number
59157      */
59158     this.defaultWidth = 100;
59159
59160     /**
59161      * Default sortable of columns which have no sortable specified (defaults to false)
59162      * @type Boolean
59163      */
59164     this.defaultSortable = false;
59165
59166     this.addEvents({
59167         /**
59168              * @event widthchange
59169              * Fires when the width of a column changes.
59170              * @param {ColumnModel} this
59171              * @param {Number} columnIndex The column index
59172              * @param {Number} newWidth The new width
59173              */
59174             "widthchange": true,
59175         /**
59176              * @event headerchange
59177              * Fires when the text of a header changes.
59178              * @param {ColumnModel} this
59179              * @param {Number} columnIndex The column index
59180              * @param {Number} newText The new header text
59181              */
59182             "headerchange": true,
59183         /**
59184              * @event hiddenchange
59185              * Fires when a column is hidden or "unhidden".
59186              * @param {ColumnModel} this
59187              * @param {Number} columnIndex The column index
59188              * @param {Boolean} hidden true if hidden, false otherwise
59189              */
59190             "hiddenchange": true,
59191             /**
59192          * @event columnmoved
59193          * Fires when a column is moved.
59194          * @param {ColumnModel} this
59195          * @param {Number} oldIndex
59196          * @param {Number} newIndex
59197          */
59198         "columnmoved" : true,
59199         /**
59200          * @event columlockchange
59201          * Fires when a column's locked state is changed
59202          * @param {ColumnModel} this
59203          * @param {Number} colIndex
59204          * @param {Boolean} locked true if locked
59205          */
59206         "columnlockchange" : true
59207     });
59208     Roo.grid.ColumnModel.superclass.constructor.call(this);
59209 };
59210 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59211     /**
59212      * @cfg {String} header The header text to display in the Grid view.
59213      */
59214         /**
59215      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59216      */
59217         /**
59218      * @cfg {String} smHeader Header at Bootsrap Small width
59219      */
59220         /**
59221      * @cfg {String} mdHeader Header at Bootsrap Medium width
59222      */
59223         /**
59224      * @cfg {String} lgHeader Header at Bootsrap Large width
59225      */
59226         /**
59227      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59228      */
59229     /**
59230      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59231      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59232      * specified, the column's index is used as an index into the Record's data Array.
59233      */
59234     /**
59235      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59236      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59237      */
59238     /**
59239      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59240      * Defaults to the value of the {@link #defaultSortable} property.
59241      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59242      */
59243     /**
59244      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59245      */
59246     /**
59247      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59248      */
59249     /**
59250      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59251      */
59252     /**
59253      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59254      */
59255     /**
59256      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59257      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59258      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59259      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59260      */
59261        /**
59262      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59263      */
59264     /**
59265      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59266      */
59267     /**
59268      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59269      */
59270     /**
59271      * @cfg {String} cursor (Optional)
59272      */
59273     /**
59274      * @cfg {String} tooltip (Optional)
59275      */
59276     /**
59277      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59278      */
59279     /**
59280      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59281      */
59282     /**
59283      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59284      */
59285     /**
59286      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59287      */
59288         /**
59289      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59290      */
59291     /**
59292      * Returns the id of the column at the specified index.
59293      * @param {Number} index The column index
59294      * @return {String} the id
59295      */
59296     getColumnId : function(index){
59297         return this.config[index].id;
59298     },
59299
59300     /**
59301      * Returns the column for a specified id.
59302      * @param {String} id The column id
59303      * @return {Object} the column
59304      */
59305     getColumnById : function(id){
59306         return this.lookup[id];
59307     },
59308
59309     
59310     /**
59311      * Returns the column Object for a specified dataIndex.
59312      * @param {String} dataIndex The column dataIndex
59313      * @return {Object|Boolean} the column or false if not found
59314      */
59315     getColumnByDataIndex: function(dataIndex){
59316         var index = this.findColumnIndex(dataIndex);
59317         return index > -1 ? this.config[index] : false;
59318     },
59319     
59320     /**
59321      * Returns the index for a specified column id.
59322      * @param {String} id The column id
59323      * @return {Number} the index, or -1 if not found
59324      */
59325     getIndexById : function(id){
59326         for(var i = 0, len = this.config.length; i < len; i++){
59327             if(this.config[i].id == id){
59328                 return i;
59329             }
59330         }
59331         return -1;
59332     },
59333     
59334     /**
59335      * Returns the index for a specified column dataIndex.
59336      * @param {String} dataIndex The column dataIndex
59337      * @return {Number} the index, or -1 if not found
59338      */
59339     
59340     findColumnIndex : function(dataIndex){
59341         for(var i = 0, len = this.config.length; i < len; i++){
59342             if(this.config[i].dataIndex == dataIndex){
59343                 return i;
59344             }
59345         }
59346         return -1;
59347     },
59348     
59349     
59350     moveColumn : function(oldIndex, newIndex){
59351         var c = this.config[oldIndex];
59352         this.config.splice(oldIndex, 1);
59353         this.config.splice(newIndex, 0, c);
59354         this.dataMap = null;
59355         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59356     },
59357
59358     isLocked : function(colIndex){
59359         return this.config[colIndex].locked === true;
59360     },
59361
59362     setLocked : function(colIndex, value, suppressEvent){
59363         if(this.isLocked(colIndex) == value){
59364             return;
59365         }
59366         this.config[colIndex].locked = value;
59367         if(!suppressEvent){
59368             this.fireEvent("columnlockchange", this, colIndex, value);
59369         }
59370     },
59371
59372     getTotalLockedWidth : function(){
59373         var totalWidth = 0;
59374         for(var i = 0; i < this.config.length; i++){
59375             if(this.isLocked(i) && !this.isHidden(i)){
59376                 this.totalWidth += this.getColumnWidth(i);
59377             }
59378         }
59379         return totalWidth;
59380     },
59381
59382     getLockedCount : function(){
59383         for(var i = 0, len = this.config.length; i < len; i++){
59384             if(!this.isLocked(i)){
59385                 return i;
59386             }
59387         }
59388         
59389         return this.config.length;
59390     },
59391
59392     /**
59393      * Returns the number of columns.
59394      * @return {Number}
59395      */
59396     getColumnCount : function(visibleOnly){
59397         if(visibleOnly === true){
59398             var c = 0;
59399             for(var i = 0, len = this.config.length; i < len; i++){
59400                 if(!this.isHidden(i)){
59401                     c++;
59402                 }
59403             }
59404             return c;
59405         }
59406         return this.config.length;
59407     },
59408
59409     /**
59410      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59411      * @param {Function} fn
59412      * @param {Object} scope (optional)
59413      * @return {Array} result
59414      */
59415     getColumnsBy : function(fn, scope){
59416         var r = [];
59417         for(var i = 0, len = this.config.length; i < len; i++){
59418             var c = this.config[i];
59419             if(fn.call(scope||this, c, i) === true){
59420                 r[r.length] = c;
59421             }
59422         }
59423         return r;
59424     },
59425
59426     /**
59427      * Returns true if the specified column is sortable.
59428      * @param {Number} col The column index
59429      * @return {Boolean}
59430      */
59431     isSortable : function(col){
59432         if(typeof this.config[col].sortable == "undefined"){
59433             return this.defaultSortable;
59434         }
59435         return this.config[col].sortable;
59436     },
59437
59438     /**
59439      * Returns the rendering (formatting) function defined for the column.
59440      * @param {Number} col The column index.
59441      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
59442      */
59443     getRenderer : function(col){
59444         if(!this.config[col].renderer){
59445             return Roo.grid.ColumnModel.defaultRenderer;
59446         }
59447         return this.config[col].renderer;
59448     },
59449
59450     /**
59451      * Sets the rendering (formatting) function for a column.
59452      * @param {Number} col The column index
59453      * @param {Function} fn The function to use to process the cell's raw data
59454      * to return HTML markup for the grid view. The render function is called with
59455      * the following parameters:<ul>
59456      * <li>Data value.</li>
59457      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
59458      * <li>css A CSS style string to apply to the table cell.</li>
59459      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
59460      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
59461      * <li>Row index</li>
59462      * <li>Column index</li>
59463      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
59464      */
59465     setRenderer : function(col, fn){
59466         this.config[col].renderer = fn;
59467     },
59468
59469     /**
59470      * Returns the width for the specified column.
59471      * @param {Number} col The column index
59472      * @param (optional) {String} gridSize bootstrap width size.
59473      * @return {Number}
59474      */
59475     getColumnWidth : function(col, gridSize)
59476         {
59477                 var cfg = this.config[col];
59478                 
59479                 if (typeof(gridSize) == 'undefined') {
59480                         return cfg.width * 1 || this.defaultWidth;
59481                 }
59482                 if (gridSize === false) { // if we set it..
59483                         return cfg.width || false;
59484                 }
59485                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
59486                 
59487                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
59488                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
59489                                 continue;
59490                         }
59491                         return cfg[ sizes[i] ];
59492                 }
59493                 return 1;
59494                 
59495     },
59496
59497     /**
59498      * Sets the width for a column.
59499      * @param {Number} col The column index
59500      * @param {Number} width The new width
59501      */
59502     setColumnWidth : function(col, width, suppressEvent){
59503         this.config[col].width = width;
59504         this.totalWidth = null;
59505         if(!suppressEvent){
59506              this.fireEvent("widthchange", this, col, width);
59507         }
59508     },
59509
59510     /**
59511      * Returns the total width of all columns.
59512      * @param {Boolean} includeHidden True to include hidden column widths
59513      * @return {Number}
59514      */
59515     getTotalWidth : function(includeHidden){
59516         if(!this.totalWidth){
59517             this.totalWidth = 0;
59518             for(var i = 0, len = this.config.length; i < len; i++){
59519                 if(includeHidden || !this.isHidden(i)){
59520                     this.totalWidth += this.getColumnWidth(i);
59521                 }
59522             }
59523         }
59524         return this.totalWidth;
59525     },
59526
59527     /**
59528      * Returns the header for the specified column.
59529      * @param {Number} col The column index
59530      * @return {String}
59531      */
59532     getColumnHeader : function(col){
59533         return this.config[col].header;
59534     },
59535
59536     /**
59537      * Sets the header for a column.
59538      * @param {Number} col The column index
59539      * @param {String} header The new header
59540      */
59541     setColumnHeader : function(col, header){
59542         this.config[col].header = header;
59543         this.fireEvent("headerchange", this, col, header);
59544     },
59545
59546     /**
59547      * Returns the tooltip for the specified column.
59548      * @param {Number} col The column index
59549      * @return {String}
59550      */
59551     getColumnTooltip : function(col){
59552             return this.config[col].tooltip;
59553     },
59554     /**
59555      * Sets the tooltip for a column.
59556      * @param {Number} col The column index
59557      * @param {String} tooltip The new tooltip
59558      */
59559     setColumnTooltip : function(col, tooltip){
59560             this.config[col].tooltip = tooltip;
59561     },
59562
59563     /**
59564      * Returns the dataIndex for the specified column.
59565      * @param {Number} col The column index
59566      * @return {Number}
59567      */
59568     getDataIndex : function(col){
59569         return this.config[col].dataIndex;
59570     },
59571
59572     /**
59573      * Sets the dataIndex for a column.
59574      * @param {Number} col The column index
59575      * @param {Number} dataIndex The new dataIndex
59576      */
59577     setDataIndex : function(col, dataIndex){
59578         this.config[col].dataIndex = dataIndex;
59579     },
59580
59581     
59582     
59583     /**
59584      * Returns true if the cell is editable.
59585      * @param {Number} colIndex The column index
59586      * @param {Number} rowIndex The row index - this is nto actually used..?
59587      * @return {Boolean}
59588      */
59589     isCellEditable : function(colIndex, rowIndex){
59590         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
59591     },
59592
59593     /**
59594      * Returns the editor defined for the cell/column.
59595      * return false or null to disable editing.
59596      * @param {Number} colIndex The column index
59597      * @param {Number} rowIndex The row index
59598      * @return {Object}
59599      */
59600     getCellEditor : function(colIndex, rowIndex){
59601         return this.config[colIndex].editor;
59602     },
59603
59604     /**
59605      * Sets if a column is editable.
59606      * @param {Number} col The column index
59607      * @param {Boolean} editable True if the column is editable
59608      */
59609     setEditable : function(col, editable){
59610         this.config[col].editable = editable;
59611     },
59612
59613
59614     /**
59615      * Returns true if the column is hidden.
59616      * @param {Number} colIndex The column index
59617      * @return {Boolean}
59618      */
59619     isHidden : function(colIndex){
59620         return this.config[colIndex].hidden;
59621     },
59622
59623
59624     /**
59625      * Returns true if the column width cannot be changed
59626      */
59627     isFixed : function(colIndex){
59628         return this.config[colIndex].fixed;
59629     },
59630
59631     /**
59632      * Returns true if the column can be resized
59633      * @return {Boolean}
59634      */
59635     isResizable : function(colIndex){
59636         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
59637     },
59638     /**
59639      * Sets if a column is hidden.
59640      * @param {Number} colIndex The column index
59641      * @param {Boolean} hidden True if the column is hidden
59642      */
59643     setHidden : function(colIndex, hidden){
59644         this.config[colIndex].hidden = hidden;
59645         this.totalWidth = null;
59646         this.fireEvent("hiddenchange", this, colIndex, hidden);
59647     },
59648
59649     /**
59650      * Sets the editor for a column.
59651      * @param {Number} col The column index
59652      * @param {Object} editor The editor object
59653      */
59654     setEditor : function(col, editor){
59655         this.config[col].editor = editor;
59656     },
59657     /**
59658      * Add a column (experimental...) - defaults to adding to the end..
59659      * @param {Object} config 
59660     */
59661     addColumn : function(c)
59662     {
59663     
59664         var i = this.config.length;
59665         this.config[i] = c;
59666         
59667         if(typeof c.dataIndex == "undefined"){
59668             c.dataIndex = i;
59669         }
59670         if(typeof c.renderer == "string"){
59671             c.renderer = Roo.util.Format[c.renderer];
59672         }
59673         if(typeof c.id == "undefined"){
59674             c.id = Roo.id();
59675         }
59676         if(c.editor && c.editor.xtype){
59677             c.editor  = Roo.factory(c.editor, Roo.grid);
59678         }
59679         if(c.editor && c.editor.isFormField){
59680             c.editor = new Roo.grid.GridEditor(c.editor);
59681         }
59682         this.lookup[c.id] = c;
59683     }
59684     
59685 });
59686
59687 Roo.grid.ColumnModel.defaultRenderer = function(value)
59688 {
59689     if(typeof value == "object") {
59690         return value;
59691     }
59692         if(typeof value == "string" && value.length < 1){
59693             return "&#160;";
59694         }
59695     
59696         return String.format("{0}", value);
59697 };
59698
59699 // Alias for backwards compatibility
59700 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
59701 /*
59702  * Based on:
59703  * Ext JS Library 1.1.1
59704  * Copyright(c) 2006-2007, Ext JS, LLC.
59705  *
59706  * Originally Released Under LGPL - original licence link has changed is not relivant.
59707  *
59708  * Fork - LGPL
59709  * <script type="text/javascript">
59710  */
59711
59712 /**
59713  * @class Roo.grid.AbstractSelectionModel
59714  * @extends Roo.util.Observable
59715  * @abstract
59716  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59717  * implemented by descendant classes.  This class should not be directly instantiated.
59718  * @constructor
59719  */
59720 Roo.grid.AbstractSelectionModel = function(){
59721     this.locked = false;
59722     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59723 };
59724
59725 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59726     /** @ignore Called by the grid automatically. Do not call directly. */
59727     init : function(grid){
59728         this.grid = grid;
59729         this.initEvents();
59730     },
59731
59732     /**
59733      * Locks the selections.
59734      */
59735     lock : function(){
59736         this.locked = true;
59737     },
59738
59739     /**
59740      * Unlocks the selections.
59741      */
59742     unlock : function(){
59743         this.locked = false;
59744     },
59745
59746     /**
59747      * Returns true if the selections are locked.
59748      * @return {Boolean}
59749      */
59750     isLocked : function(){
59751         return this.locked;
59752     }
59753 });/*
59754  * Based on:
59755  * Ext JS Library 1.1.1
59756  * Copyright(c) 2006-2007, Ext JS, LLC.
59757  *
59758  * Originally Released Under LGPL - original licence link has changed is not relivant.
59759  *
59760  * Fork - LGPL
59761  * <script type="text/javascript">
59762  */
59763 /**
59764  * @extends Roo.grid.AbstractSelectionModel
59765  * @class Roo.grid.RowSelectionModel
59766  * The default SelectionModel used by {@link Roo.grid.Grid}.
59767  * It supports multiple selections and keyboard selection/navigation. 
59768  * @constructor
59769  * @param {Object} config
59770  */
59771 Roo.grid.RowSelectionModel = function(config){
59772     Roo.apply(this, config);
59773     this.selections = new Roo.util.MixedCollection(false, function(o){
59774         return o.id;
59775     });
59776
59777     this.last = false;
59778     this.lastActive = false;
59779
59780     this.addEvents({
59781         /**
59782         * @event selectionchange
59783         * Fires when the selection changes
59784         * @param {SelectionModel} this
59785         */
59786        "selectionchange" : true,
59787        /**
59788         * @event afterselectionchange
59789         * Fires after the selection changes (eg. by key press or clicking)
59790         * @param {SelectionModel} this
59791         */
59792        "afterselectionchange" : true,
59793        /**
59794         * @event beforerowselect
59795         * Fires when a row is selected being selected, return false to cancel.
59796         * @param {SelectionModel} this
59797         * @param {Number} rowIndex The selected index
59798         * @param {Boolean} keepExisting False if other selections will be cleared
59799         */
59800        "beforerowselect" : true,
59801        /**
59802         * @event rowselect
59803         * Fires when a row is selected.
59804         * @param {SelectionModel} this
59805         * @param {Number} rowIndex The selected index
59806         * @param {Roo.data.Record} r The record
59807         */
59808        "rowselect" : true,
59809        /**
59810         * @event rowdeselect
59811         * Fires when a row is deselected.
59812         * @param {SelectionModel} this
59813         * @param {Number} rowIndex The selected index
59814         */
59815         "rowdeselect" : true
59816     });
59817     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59818     this.locked = false;
59819 };
59820
59821 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59822     /**
59823      * @cfg {Boolean} singleSelect
59824      * True to allow selection of only one row at a time (defaults to false)
59825      */
59826     singleSelect : false,
59827
59828     // private
59829     initEvents : function(){
59830
59831         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59832             this.grid.on("mousedown", this.handleMouseDown, this);
59833         }else{ // allow click to work like normal
59834             this.grid.on("rowclick", this.handleDragableRowClick, this);
59835         }
59836         // bootstrap does not have a view..
59837         var view = this.grid.view ? this.grid.view : this.grid;
59838         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59839             "up" : function(e){
59840                 if(!e.shiftKey){
59841                     this.selectPrevious(e.shiftKey);
59842                 }else if(this.last !== false && this.lastActive !== false){
59843                     var last = this.last;
59844                     this.selectRange(this.last,  this.lastActive-1);
59845                     view.focusRow(this.lastActive);
59846                     if(last !== false){
59847                         this.last = last;
59848                     }
59849                 }else{
59850                     this.selectFirstRow();
59851                 }
59852                 this.fireEvent("afterselectionchange", this);
59853             },
59854             "down" : function(e){
59855                 if(!e.shiftKey){
59856                     this.selectNext(e.shiftKey);
59857                 }else if(this.last !== false && this.lastActive !== false){
59858                     var last = this.last;
59859                     this.selectRange(this.last,  this.lastActive+1);
59860                     view.focusRow(this.lastActive);
59861                     if(last !== false){
59862                         this.last = last;
59863                     }
59864                 }else{
59865                     this.selectFirstRow();
59866                 }
59867                 this.fireEvent("afterselectionchange", this);
59868             },
59869             scope: this
59870         });
59871
59872          
59873         view.on("refresh", this.onRefresh, this);
59874         view.on("rowupdated", this.onRowUpdated, this);
59875         view.on("rowremoved", this.onRemove, this);
59876     },
59877
59878     // private
59879     onRefresh : function(){
59880         var ds = this.grid.ds, i, v = this.grid.view;
59881         var s = this.selections;
59882         s.each(function(r){
59883             if((i = ds.indexOfId(r.id)) != -1){
59884                 v.onRowSelect(i);
59885                 s.add(ds.getAt(i)); // updating the selection relate data
59886             }else{
59887                 s.remove(r);
59888             }
59889         });
59890     },
59891
59892     // private
59893     onRemove : function(v, index, r){
59894         this.selections.remove(r);
59895     },
59896
59897     // private
59898     onRowUpdated : function(v, index, r){
59899         if(this.isSelected(r)){
59900             v.onRowSelect(index);
59901         }
59902     },
59903
59904     /**
59905      * Select records.
59906      * @param {Array} records The records to select
59907      * @param {Boolean} keepExisting (optional) True to keep existing selections
59908      */
59909     selectRecords : function(records, keepExisting){
59910         if(!keepExisting){
59911             this.clearSelections();
59912         }
59913         var ds = this.grid.ds;
59914         for(var i = 0, len = records.length; i < len; i++){
59915             this.selectRow(ds.indexOf(records[i]), true);
59916         }
59917     },
59918
59919     /**
59920      * Gets the number of selected rows.
59921      * @return {Number}
59922      */
59923     getCount : function(){
59924         return this.selections.length;
59925     },
59926
59927     /**
59928      * Selects the first row in the grid.
59929      */
59930     selectFirstRow : function(){
59931         this.selectRow(0);
59932     },
59933
59934     /**
59935      * Select the last row.
59936      * @param {Boolean} keepExisting (optional) True to keep existing selections
59937      */
59938     selectLastRow : function(keepExisting){
59939         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59940     },
59941
59942     /**
59943      * Selects the row immediately following the last selected row.
59944      * @param {Boolean} keepExisting (optional) True to keep existing selections
59945      */
59946     selectNext : function(keepExisting){
59947         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59948             this.selectRow(this.last+1, keepExisting);
59949             var view = this.grid.view ? this.grid.view : this.grid;
59950             view.focusRow(this.last);
59951         }
59952     },
59953
59954     /**
59955      * Selects the row that precedes the last selected row.
59956      * @param {Boolean} keepExisting (optional) True to keep existing selections
59957      */
59958     selectPrevious : function(keepExisting){
59959         if(this.last){
59960             this.selectRow(this.last-1, keepExisting);
59961             var view = this.grid.view ? this.grid.view : this.grid;
59962             view.focusRow(this.last);
59963         }
59964     },
59965
59966     /**
59967      * Returns the selected records
59968      * @return {Array} Array of selected records
59969      */
59970     getSelections : function(){
59971         return [].concat(this.selections.items);
59972     },
59973
59974     /**
59975      * Returns the first selected record.
59976      * @return {Record}
59977      */
59978     getSelected : function(){
59979         return this.selections.itemAt(0);
59980     },
59981
59982
59983     /**
59984      * Clears all selections.
59985      */
59986     clearSelections : function(fast){
59987         if(this.locked) {
59988             return;
59989         }
59990         if(fast !== true){
59991             var ds = this.grid.ds;
59992             var s = this.selections;
59993             s.each(function(r){
59994                 this.deselectRow(ds.indexOfId(r.id));
59995             }, this);
59996             s.clear();
59997         }else{
59998             this.selections.clear();
59999         }
60000         this.last = false;
60001     },
60002
60003
60004     /**
60005      * Selects all rows.
60006      */
60007     selectAll : function(){
60008         if(this.locked) {
60009             return;
60010         }
60011         this.selections.clear();
60012         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60013             this.selectRow(i, true);
60014         }
60015     },
60016
60017     /**
60018      * Returns True if there is a selection.
60019      * @return {Boolean}
60020      */
60021     hasSelection : function(){
60022         return this.selections.length > 0;
60023     },
60024
60025     /**
60026      * Returns True if the specified row is selected.
60027      * @param {Number/Record} record The record or index of the record to check
60028      * @return {Boolean}
60029      */
60030     isSelected : function(index){
60031         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60032         return (r && this.selections.key(r.id) ? true : false);
60033     },
60034
60035     /**
60036      * Returns True if the specified record id is selected.
60037      * @param {String} id The id of record to check
60038      * @return {Boolean}
60039      */
60040     isIdSelected : function(id){
60041         return (this.selections.key(id) ? true : false);
60042     },
60043
60044     // private
60045     handleMouseDown : function(e, t)
60046     {
60047         var view = this.grid.view ? this.grid.view : this.grid;
60048         var rowIndex;
60049         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60050             return;
60051         };
60052         if(e.shiftKey && this.last !== false){
60053             var last = this.last;
60054             this.selectRange(last, rowIndex, e.ctrlKey);
60055             this.last = last; // reset the last
60056             view.focusRow(rowIndex);
60057         }else{
60058             var isSelected = this.isSelected(rowIndex);
60059             if(e.button !== 0 && isSelected){
60060                 view.focusRow(rowIndex);
60061             }else if(e.ctrlKey && isSelected){
60062                 this.deselectRow(rowIndex);
60063             }else if(!isSelected){
60064                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60065                 view.focusRow(rowIndex);
60066             }
60067         }
60068         this.fireEvent("afterselectionchange", this);
60069     },
60070     // private
60071     handleDragableRowClick :  function(grid, rowIndex, e) 
60072     {
60073         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60074             this.selectRow(rowIndex, false);
60075             var view = this.grid.view ? this.grid.view : this.grid;
60076             view.focusRow(rowIndex);
60077              this.fireEvent("afterselectionchange", this);
60078         }
60079     },
60080     
60081     /**
60082      * Selects multiple rows.
60083      * @param {Array} rows Array of the indexes of the row to select
60084      * @param {Boolean} keepExisting (optional) True to keep existing selections
60085      */
60086     selectRows : function(rows, keepExisting){
60087         if(!keepExisting){
60088             this.clearSelections();
60089         }
60090         for(var i = 0, len = rows.length; i < len; i++){
60091             this.selectRow(rows[i], true);
60092         }
60093     },
60094
60095     /**
60096      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60097      * @param {Number} startRow The index of the first row in the range
60098      * @param {Number} endRow The index of the last row in the range
60099      * @param {Boolean} keepExisting (optional) True to retain existing selections
60100      */
60101     selectRange : function(startRow, endRow, keepExisting){
60102         if(this.locked) {
60103             return;
60104         }
60105         if(!keepExisting){
60106             this.clearSelections();
60107         }
60108         if(startRow <= endRow){
60109             for(var i = startRow; i <= endRow; i++){
60110                 this.selectRow(i, true);
60111             }
60112         }else{
60113             for(var i = startRow; i >= endRow; i--){
60114                 this.selectRow(i, true);
60115             }
60116         }
60117     },
60118
60119     /**
60120      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60121      * @param {Number} startRow The index of the first row in the range
60122      * @param {Number} endRow The index of the last row in the range
60123      */
60124     deselectRange : function(startRow, endRow, preventViewNotify){
60125         if(this.locked) {
60126             return;
60127         }
60128         for(var i = startRow; i <= endRow; i++){
60129             this.deselectRow(i, preventViewNotify);
60130         }
60131     },
60132
60133     /**
60134      * Selects a row.
60135      * @param {Number} row The index of the row to select
60136      * @param {Boolean} keepExisting (optional) True to keep existing selections
60137      */
60138     selectRow : function(index, keepExisting, preventViewNotify){
60139         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60140             return;
60141         }
60142         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60143             if(!keepExisting || this.singleSelect){
60144                 this.clearSelections();
60145             }
60146             var r = this.grid.ds.getAt(index);
60147             this.selections.add(r);
60148             this.last = this.lastActive = index;
60149             if(!preventViewNotify){
60150                 var view = this.grid.view ? this.grid.view : this.grid;
60151                 view.onRowSelect(index);
60152             }
60153             this.fireEvent("rowselect", this, index, r);
60154             this.fireEvent("selectionchange", this);
60155         }
60156     },
60157
60158     /**
60159      * Deselects a row.
60160      * @param {Number} row The index of the row to deselect
60161      */
60162     deselectRow : function(index, preventViewNotify){
60163         if(this.locked) {
60164             return;
60165         }
60166         if(this.last == index){
60167             this.last = false;
60168         }
60169         if(this.lastActive == index){
60170             this.lastActive = false;
60171         }
60172         var r = this.grid.ds.getAt(index);
60173         this.selections.remove(r);
60174         if(!preventViewNotify){
60175             var view = this.grid.view ? this.grid.view : this.grid;
60176             view.onRowDeselect(index);
60177         }
60178         this.fireEvent("rowdeselect", this, index);
60179         this.fireEvent("selectionchange", this);
60180     },
60181
60182     // private
60183     restoreLast : function(){
60184         if(this._last){
60185             this.last = this._last;
60186         }
60187     },
60188
60189     // private
60190     acceptsNav : function(row, col, cm){
60191         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60192     },
60193
60194     // private
60195     onEditorKey : function(field, e){
60196         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60197         if(k == e.TAB){
60198             e.stopEvent();
60199             ed.completeEdit();
60200             if(e.shiftKey){
60201                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60202             }else{
60203                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60204             }
60205         }else if(k == e.ENTER && !e.ctrlKey){
60206             e.stopEvent();
60207             ed.completeEdit();
60208             if(e.shiftKey){
60209                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60210             }else{
60211                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60212             }
60213         }else if(k == e.ESC){
60214             ed.cancelEdit();
60215         }
60216         if(newCell){
60217             g.startEditing(newCell[0], newCell[1]);
60218         }
60219     }
60220 });/*
60221  * Based on:
60222  * Ext JS Library 1.1.1
60223  * Copyright(c) 2006-2007, Ext JS, LLC.
60224  *
60225  * Originally Released Under LGPL - original licence link has changed is not relivant.
60226  *
60227  * Fork - LGPL
60228  * <script type="text/javascript">
60229  */
60230 /**
60231  * @class Roo.grid.CellSelectionModel
60232  * @extends Roo.grid.AbstractSelectionModel
60233  * This class provides the basic implementation for cell selection in a grid.
60234  * @constructor
60235  * @param {Object} config The object containing the configuration of this model.
60236  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60237  */
60238 Roo.grid.CellSelectionModel = function(config){
60239     Roo.apply(this, config);
60240
60241     this.selection = null;
60242
60243     this.addEvents({
60244         /**
60245              * @event beforerowselect
60246              * Fires before a cell is selected.
60247              * @param {SelectionModel} this
60248              * @param {Number} rowIndex The selected row index
60249              * @param {Number} colIndex The selected cell index
60250              */
60251             "beforecellselect" : true,
60252         /**
60253              * @event cellselect
60254              * Fires when 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             "cellselect" : true,
60260         /**
60261              * @event selectionchange
60262              * Fires when the active selection changes.
60263              * @param {SelectionModel} this
60264              * @param {Object} selection null for no selection or an object (o) with two properties
60265                 <ul>
60266                 <li>o.record: the record object for the row the selection is in</li>
60267                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60268                 </ul>
60269              */
60270             "selectionchange" : true,
60271         /**
60272              * @event tabend
60273              * Fires when the tab (or enter) was pressed on the last editable cell
60274              * You can use this to trigger add new row.
60275              * @param {SelectionModel} this
60276              */
60277             "tabend" : true,
60278          /**
60279              * @event beforeeditnext
60280              * Fires before the next editable sell is made active
60281              * You can use this to skip to another cell or fire the tabend
60282              *    if you set cell to false
60283              * @param {Object} eventdata object : { cell : [ row, col ] } 
60284              */
60285             "beforeeditnext" : true
60286     });
60287     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60288 };
60289
60290 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60291     
60292     enter_is_tab: false,
60293
60294     /** @ignore */
60295     initEvents : function(){
60296         this.grid.on("mousedown", this.handleMouseDown, this);
60297         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60298         var view = this.grid.view;
60299         view.on("refresh", this.onViewChange, this);
60300         view.on("rowupdated", this.onRowUpdated, this);
60301         view.on("beforerowremoved", this.clearSelections, this);
60302         view.on("beforerowsinserted", this.clearSelections, this);
60303         if(this.grid.isEditor){
60304             this.grid.on("beforeedit", this.beforeEdit,  this);
60305         }
60306     },
60307
60308         //private
60309     beforeEdit : function(e){
60310         this.select(e.row, e.column, false, true, e.record);
60311     },
60312
60313         //private
60314     onRowUpdated : function(v, index, r){
60315         if(this.selection && this.selection.record == r){
60316             v.onCellSelect(index, this.selection.cell[1]);
60317         }
60318     },
60319
60320         //private
60321     onViewChange : function(){
60322         this.clearSelections(true);
60323     },
60324
60325         /**
60326          * Returns the currently selected cell,.
60327          * @return {Array} The selected cell (row, column) or null if none selected.
60328          */
60329     getSelectedCell : function(){
60330         return this.selection ? this.selection.cell : null;
60331     },
60332
60333     /**
60334      * Clears all selections.
60335      * @param {Boolean} true to prevent the gridview from being notified about the change.
60336      */
60337     clearSelections : function(preventNotify){
60338         var s = this.selection;
60339         if(s){
60340             if(preventNotify !== true){
60341                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60342             }
60343             this.selection = null;
60344             this.fireEvent("selectionchange", this, null);
60345         }
60346     },
60347
60348     /**
60349      * Returns true if there is a selection.
60350      * @return {Boolean}
60351      */
60352     hasSelection : function(){
60353         return this.selection ? true : false;
60354     },
60355
60356     /** @ignore */
60357     handleMouseDown : function(e, t){
60358         var v = this.grid.getView();
60359         if(this.isLocked()){
60360             return;
60361         };
60362         var row = v.findRowIndex(t);
60363         var cell = v.findCellIndex(t);
60364         if(row !== false && cell !== false){
60365             this.select(row, cell);
60366         }
60367     },
60368
60369     /**
60370      * Selects a cell.
60371      * @param {Number} rowIndex
60372      * @param {Number} collIndex
60373      */
60374     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60375         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60376             this.clearSelections();
60377             r = r || this.grid.dataSource.getAt(rowIndex);
60378             this.selection = {
60379                 record : r,
60380                 cell : [rowIndex, colIndex]
60381             };
60382             if(!preventViewNotify){
60383                 var v = this.grid.getView();
60384                 v.onCellSelect(rowIndex, colIndex);
60385                 if(preventFocus !== true){
60386                     v.focusCell(rowIndex, colIndex);
60387                 }
60388             }
60389             this.fireEvent("cellselect", this, rowIndex, colIndex);
60390             this.fireEvent("selectionchange", this, this.selection);
60391         }
60392     },
60393
60394         //private
60395     isSelectable : function(rowIndex, colIndex, cm){
60396         return !cm.isHidden(colIndex);
60397     },
60398
60399     /** @ignore */
60400     handleKeyDown : function(e){
60401         //Roo.log('Cell Sel Model handleKeyDown');
60402         if(!e.isNavKeyPress()){
60403             return;
60404         }
60405         var g = this.grid, s = this.selection;
60406         if(!s){
60407             e.stopEvent();
60408             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60409             if(cell){
60410                 this.select(cell[0], cell[1]);
60411             }
60412             return;
60413         }
60414         var sm = this;
60415         var walk = function(row, col, step){
60416             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60417         };
60418         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60419         var newCell;
60420
60421       
60422
60423         switch(k){
60424             case e.TAB:
60425                 // handled by onEditorKey
60426                 if (g.isEditor && g.editing) {
60427                     return;
60428                 }
60429                 if(e.shiftKey) {
60430                     newCell = walk(r, c-1, -1);
60431                 } else {
60432                     newCell = walk(r, c+1, 1);
60433                 }
60434                 break;
60435             
60436             case e.DOWN:
60437                newCell = walk(r+1, c, 1);
60438                 break;
60439             
60440             case e.UP:
60441                 newCell = walk(r-1, c, -1);
60442                 break;
60443             
60444             case e.RIGHT:
60445                 newCell = walk(r, c+1, 1);
60446                 break;
60447             
60448             case e.LEFT:
60449                 newCell = walk(r, c-1, -1);
60450                 break;
60451             
60452             case e.ENTER:
60453                 
60454                 if(g.isEditor && !g.editing){
60455                    g.startEditing(r, c);
60456                    e.stopEvent();
60457                    return;
60458                 }
60459                 
60460                 
60461              break;
60462         };
60463         if(newCell){
60464             this.select(newCell[0], newCell[1]);
60465             e.stopEvent();
60466             
60467         }
60468     },
60469
60470     acceptsNav : function(row, col, cm){
60471         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60472     },
60473     /**
60474      * Selects a cell.
60475      * @param {Number} field (not used) - as it's normally used as a listener
60476      * @param {Number} e - event - fake it by using
60477      *
60478      * var e = Roo.EventObjectImpl.prototype;
60479      * e.keyCode = e.TAB
60480      *
60481      * 
60482      */
60483     onEditorKey : function(field, e){
60484         
60485         var k = e.getKey(),
60486             newCell,
60487             g = this.grid,
60488             ed = g.activeEditor,
60489             forward = false;
60490         ///Roo.log('onEditorKey' + k);
60491         
60492         
60493         if (this.enter_is_tab && k == e.ENTER) {
60494             k = e.TAB;
60495         }
60496         
60497         if(k == e.TAB){
60498             if(e.shiftKey){
60499                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60500             }else{
60501                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60502                 forward = true;
60503             }
60504             
60505             e.stopEvent();
60506             
60507         } else if(k == e.ENTER &&  !e.ctrlKey){
60508             ed.completeEdit();
60509             e.stopEvent();
60510             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60511         
60512                 } else if(k == e.ESC){
60513             ed.cancelEdit();
60514         }
60515                 
60516         if (newCell) {
60517             var ecall = { cell : newCell, forward : forward };
60518             this.fireEvent('beforeeditnext', ecall );
60519             newCell = ecall.cell;
60520                         forward = ecall.forward;
60521         }
60522                 
60523         if(newCell){
60524             //Roo.log('next cell after edit');
60525             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
60526         } else if (forward) {
60527             // tabbed past last
60528             this.fireEvent.defer(100, this, ['tabend',this]);
60529         }
60530     }
60531 });/*
60532  * Based on:
60533  * Ext JS Library 1.1.1
60534  * Copyright(c) 2006-2007, Ext JS, LLC.
60535  *
60536  * Originally Released Under LGPL - original licence link has changed is not relivant.
60537  *
60538  * Fork - LGPL
60539  * <script type="text/javascript">
60540  */
60541  
60542 /**
60543  * @class Roo.grid.EditorGrid
60544  * @extends Roo.grid.Grid
60545  * Class for creating and editable grid.
60546  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
60547  * The container MUST have some type of size defined for the grid to fill. The container will be 
60548  * automatically set to position relative if it isn't already.
60549  * @param {Object} dataSource The data model to bind to
60550  * @param {Object} colModel The column model with info about this grid's columns
60551  */
60552 Roo.grid.EditorGrid = function(container, config){
60553     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
60554     this.getGridEl().addClass("xedit-grid");
60555
60556     if(!this.selModel){
60557         this.selModel = new Roo.grid.CellSelectionModel();
60558     }
60559
60560     this.activeEditor = null;
60561
60562         this.addEvents({
60563             /**
60564              * @event beforeedit
60565              * Fires before cell editing is triggered. The edit event object has the following properties <br />
60566              * <ul style="padding:5px;padding-left:16px;">
60567              * <li>grid - This grid</li>
60568              * <li>record - The record being edited</li>
60569              * <li>field - The field name being edited</li>
60570              * <li>value - The value for the field being edited.</li>
60571              * <li>row - The grid row index</li>
60572              * <li>column - The grid column index</li>
60573              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60574              * </ul>
60575              * @param {Object} e An edit event (see above for description)
60576              */
60577             "beforeedit" : true,
60578             /**
60579              * @event afteredit
60580              * Fires after a cell is edited. <br />
60581              * <ul style="padding:5px;padding-left:16px;">
60582              * <li>grid - This grid</li>
60583              * <li>record - The record being edited</li>
60584              * <li>field - The field name being edited</li>
60585              * <li>value - The value being set</li>
60586              * <li>originalValue - The original value for the field, before the edit.</li>
60587              * <li>row - The grid row index</li>
60588              * <li>column - The grid column index</li>
60589              * </ul>
60590              * @param {Object} e An edit event (see above for description)
60591              */
60592             "afteredit" : true,
60593             /**
60594              * @event validateedit
60595              * Fires after a cell is edited, but before the value is set in the record. 
60596          * You can use this to modify the value being set in the field, Return false
60597              * to cancel the change. The edit event object has the following properties <br />
60598              * <ul style="padding:5px;padding-left:16px;">
60599          * <li>editor - This editor</li>
60600              * <li>grid - This grid</li>
60601              * <li>record - The record being edited</li>
60602              * <li>field - The field name being edited</li>
60603              * <li>value - The value being set</li>
60604              * <li>originalValue - The original value for the field, before the edit.</li>
60605              * <li>row - The grid row index</li>
60606              * <li>column - The grid column index</li>
60607              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60608              * </ul>
60609              * @param {Object} e An edit event (see above for description)
60610              */
60611             "validateedit" : true
60612         });
60613     this.on("bodyscroll", this.stopEditing,  this);
60614     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
60615 };
60616
60617 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
60618     /**
60619      * @cfg {Number} clicksToEdit
60620      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
60621      */
60622     clicksToEdit: 2,
60623
60624     // private
60625     isEditor : true,
60626     // private
60627     trackMouseOver: false, // causes very odd FF errors
60628
60629     onCellDblClick : function(g, row, col){
60630         this.startEditing(row, col);
60631     },
60632
60633     onEditComplete : function(ed, value, startValue){
60634         this.editing = false;
60635         this.activeEditor = null;
60636         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
60637         var r = ed.record;
60638         var field = this.colModel.getDataIndex(ed.col);
60639         var e = {
60640             grid: this,
60641             record: r,
60642             field: field,
60643             originalValue: startValue,
60644             value: value,
60645             row: ed.row,
60646             column: ed.col,
60647             cancel:false,
60648             editor: ed
60649         };
60650         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
60651         cell.show();
60652           
60653         if(String(value) !== String(startValue)){
60654             
60655             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
60656                 r.set(field, e.value);
60657                 // if we are dealing with a combo box..
60658                 // then we also set the 'name' colum to be the displayField
60659                 if (ed.field.displayField && ed.field.name) {
60660                     r.set(ed.field.name, ed.field.el.dom.value);
60661                 }
60662                 
60663                 delete e.cancel; //?? why!!!
60664                 this.fireEvent("afteredit", e);
60665             }
60666         } else {
60667             this.fireEvent("afteredit", e); // always fire it!
60668         }
60669         this.view.focusCell(ed.row, ed.col);
60670     },
60671
60672     /**
60673      * Starts editing the specified for the specified row/column
60674      * @param {Number} rowIndex
60675      * @param {Number} colIndex
60676      */
60677     startEditing : function(row, col){
60678         this.stopEditing();
60679         if(this.colModel.isCellEditable(col, row)){
60680             this.view.ensureVisible(row, col, true);
60681           
60682             var r = this.dataSource.getAt(row);
60683             var field = this.colModel.getDataIndex(col);
60684             var cell = Roo.get(this.view.getCell(row,col));
60685             var e = {
60686                 grid: this,
60687                 record: r,
60688                 field: field,
60689                 value: r.data[field],
60690                 row: row,
60691                 column: col,
60692                 cancel:false 
60693             };
60694             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
60695                 this.editing = true;
60696                 var ed = this.colModel.getCellEditor(col, row);
60697                 
60698                 if (!ed) {
60699                     return;
60700                 }
60701                 if(!ed.rendered){
60702                     ed.render(ed.parentEl || document.body);
60703                 }
60704                 ed.field.reset();
60705                
60706                 cell.hide();
60707                 
60708                 (function(){ // complex but required for focus issues in safari, ie and opera
60709                     ed.row = row;
60710                     ed.col = col;
60711                     ed.record = r;
60712                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
60713                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60714                     this.activeEditor = ed;
60715                     var v = r.data[field];
60716                     ed.startEdit(this.view.getCell(row, col), v);
60717                     // combo's with 'displayField and name set
60718                     if (ed.field.displayField && ed.field.name) {
60719                         ed.field.el.dom.value = r.data[ed.field.name];
60720                     }
60721                     
60722                     
60723                 }).defer(50, this);
60724             }
60725         }
60726     },
60727         
60728     /**
60729      * Stops any active editing
60730      */
60731     stopEditing : function(){
60732         if(this.activeEditor){
60733             this.activeEditor.completeEdit();
60734         }
60735         this.activeEditor = null;
60736     },
60737         
60738          /**
60739      * Called to get grid's drag proxy text, by default returns this.ddText.
60740      * @return {String}
60741      */
60742     getDragDropText : function(){
60743         var count = this.selModel.getSelectedCell() ? 1 : 0;
60744         return String.format(this.ddText, count, count == 1 ? '' : 's');
60745     }
60746         
60747 });/*
60748  * Based on:
60749  * Ext JS Library 1.1.1
60750  * Copyright(c) 2006-2007, Ext JS, LLC.
60751  *
60752  * Originally Released Under LGPL - original licence link has changed is not relivant.
60753  *
60754  * Fork - LGPL
60755  * <script type="text/javascript">
60756  */
60757
60758 // private - not really -- you end up using it !
60759 // This is a support class used internally by the Grid components
60760
60761 /**
60762  * @class Roo.grid.GridEditor
60763  * @extends Roo.Editor
60764  * Class for creating and editable grid elements.
60765  * @param {Object} config any settings (must include field)
60766  */
60767 Roo.grid.GridEditor = function(field, config){
60768     if (!config && field.field) {
60769         config = field;
60770         field = Roo.factory(config.field, Roo.form);
60771     }
60772     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60773     field.monitorTab = false;
60774 };
60775
60776 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60777     
60778     /**
60779      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60780      */
60781     
60782     alignment: "tl-tl",
60783     autoSize: "width",
60784     hideEl : false,
60785     cls: "x-small-editor x-grid-editor",
60786     shim:false,
60787     shadow:"frame"
60788 });/*
60789  * Based on:
60790  * Ext JS Library 1.1.1
60791  * Copyright(c) 2006-2007, Ext JS, LLC.
60792  *
60793  * Originally Released Under LGPL - original licence link has changed is not relivant.
60794  *
60795  * Fork - LGPL
60796  * <script type="text/javascript">
60797  */
60798   
60799
60800   
60801 Roo.grid.PropertyRecord = Roo.data.Record.create([
60802     {name:'name',type:'string'},  'value'
60803 ]);
60804
60805
60806 Roo.grid.PropertyStore = function(grid, source){
60807     this.grid = grid;
60808     this.store = new Roo.data.Store({
60809         recordType : Roo.grid.PropertyRecord
60810     });
60811     this.store.on('update', this.onUpdate,  this);
60812     if(source){
60813         this.setSource(source);
60814     }
60815     Roo.grid.PropertyStore.superclass.constructor.call(this);
60816 };
60817
60818
60819
60820 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60821     setSource : function(o){
60822         this.source = o;
60823         this.store.removeAll();
60824         var data = [];
60825         for(var k in o){
60826             if(this.isEditableValue(o[k])){
60827                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60828             }
60829         }
60830         this.store.loadRecords({records: data}, {}, true);
60831     },
60832
60833     onUpdate : function(ds, record, type){
60834         if(type == Roo.data.Record.EDIT){
60835             var v = record.data['value'];
60836             var oldValue = record.modified['value'];
60837             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60838                 this.source[record.id] = v;
60839                 record.commit();
60840                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60841             }else{
60842                 record.reject();
60843             }
60844         }
60845     },
60846
60847     getProperty : function(row){
60848        return this.store.getAt(row);
60849     },
60850
60851     isEditableValue: function(val){
60852         if(val && val instanceof Date){
60853             return true;
60854         }else if(typeof val == 'object' || typeof val == 'function'){
60855             return false;
60856         }
60857         return true;
60858     },
60859
60860     setValue : function(prop, value){
60861         this.source[prop] = value;
60862         this.store.getById(prop).set('value', value);
60863     },
60864
60865     getSource : function(){
60866         return this.source;
60867     }
60868 });
60869
60870 Roo.grid.PropertyColumnModel = function(grid, store){
60871     this.grid = grid;
60872     var g = Roo.grid;
60873     g.PropertyColumnModel.superclass.constructor.call(this, [
60874         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60875         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60876     ]);
60877     this.store = store;
60878     this.bselect = Roo.DomHelper.append(document.body, {
60879         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60880             {tag: 'option', value: 'true', html: 'true'},
60881             {tag: 'option', value: 'false', html: 'false'}
60882         ]
60883     });
60884     Roo.id(this.bselect);
60885     var f = Roo.form;
60886     this.editors = {
60887         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60888         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60889         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60890         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60891         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60892     };
60893     this.renderCellDelegate = this.renderCell.createDelegate(this);
60894     this.renderPropDelegate = this.renderProp.createDelegate(this);
60895 };
60896
60897 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60898     
60899     
60900     nameText : 'Name',
60901     valueText : 'Value',
60902     
60903     dateFormat : 'm/j/Y',
60904     
60905     
60906     renderDate : function(dateVal){
60907         return dateVal.dateFormat(this.dateFormat);
60908     },
60909
60910     renderBool : function(bVal){
60911         return bVal ? 'true' : 'false';
60912     },
60913
60914     isCellEditable : function(colIndex, rowIndex){
60915         return colIndex == 1;
60916     },
60917
60918     getRenderer : function(col){
60919         return col == 1 ?
60920             this.renderCellDelegate : this.renderPropDelegate;
60921     },
60922
60923     renderProp : function(v){
60924         return this.getPropertyName(v);
60925     },
60926
60927     renderCell : function(val){
60928         var rv = val;
60929         if(val instanceof Date){
60930             rv = this.renderDate(val);
60931         }else if(typeof val == 'boolean'){
60932             rv = this.renderBool(val);
60933         }
60934         return Roo.util.Format.htmlEncode(rv);
60935     },
60936
60937     getPropertyName : function(name){
60938         var pn = this.grid.propertyNames;
60939         return pn && pn[name] ? pn[name] : name;
60940     },
60941
60942     getCellEditor : function(colIndex, rowIndex){
60943         var p = this.store.getProperty(rowIndex);
60944         var n = p.data['name'], val = p.data['value'];
60945         
60946         if(typeof(this.grid.customEditors[n]) == 'string'){
60947             return this.editors[this.grid.customEditors[n]];
60948         }
60949         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60950             return this.grid.customEditors[n];
60951         }
60952         if(val instanceof Date){
60953             return this.editors['date'];
60954         }else if(typeof val == 'number'){
60955             return this.editors['number'];
60956         }else if(typeof val == 'boolean'){
60957             return this.editors['boolean'];
60958         }else{
60959             return this.editors['string'];
60960         }
60961     }
60962 });
60963
60964 /**
60965  * @class Roo.grid.PropertyGrid
60966  * @extends Roo.grid.EditorGrid
60967  * This class represents the  interface of a component based property grid control.
60968  * <br><br>Usage:<pre><code>
60969  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60970       
60971  });
60972  // set any options
60973  grid.render();
60974  * </code></pre>
60975   
60976  * @constructor
60977  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60978  * The container MUST have some type of size defined for the grid to fill. The container will be
60979  * automatically set to position relative if it isn't already.
60980  * @param {Object} config A config object that sets properties on this grid.
60981  */
60982 Roo.grid.PropertyGrid = function(container, config){
60983     config = config || {};
60984     var store = new Roo.grid.PropertyStore(this);
60985     this.store = store;
60986     var cm = new Roo.grid.PropertyColumnModel(this, store);
60987     store.store.sort('name', 'ASC');
60988     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60989         ds: store.store,
60990         cm: cm,
60991         enableColLock:false,
60992         enableColumnMove:false,
60993         stripeRows:false,
60994         trackMouseOver: false,
60995         clicksToEdit:1
60996     }, config));
60997     this.getGridEl().addClass('x-props-grid');
60998     this.lastEditRow = null;
60999     this.on('columnresize', this.onColumnResize, this);
61000     this.addEvents({
61001          /**
61002              * @event beforepropertychange
61003              * Fires before a property changes (return false to stop?)
61004              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61005              * @param {String} id Record Id
61006              * @param {String} newval New Value
61007          * @param {String} oldval Old Value
61008              */
61009         "beforepropertychange": true,
61010         /**
61011              * @event propertychange
61012              * Fires after a property changes
61013              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61014              * @param {String} id Record Id
61015              * @param {String} newval New Value
61016          * @param {String} oldval Old Value
61017              */
61018         "propertychange": true
61019     });
61020     this.customEditors = this.customEditors || {};
61021 };
61022 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61023     
61024      /**
61025      * @cfg {Object} customEditors map of colnames=> custom editors.
61026      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61027      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61028      * false disables editing of the field.
61029          */
61030     
61031       /**
61032      * @cfg {Object} propertyNames map of property Names to their displayed value
61033          */
61034     
61035     render : function(){
61036         Roo.grid.PropertyGrid.superclass.render.call(this);
61037         this.autoSize.defer(100, this);
61038     },
61039
61040     autoSize : function(){
61041         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61042         if(this.view){
61043             this.view.fitColumns();
61044         }
61045     },
61046
61047     onColumnResize : function(){
61048         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61049         this.autoSize();
61050     },
61051     /**
61052      * Sets the data for the Grid
61053      * accepts a Key => Value object of all the elements avaiable.
61054      * @param {Object} data  to appear in grid.
61055      */
61056     setSource : function(source){
61057         this.store.setSource(source);
61058         //this.autoSize();
61059     },
61060     /**
61061      * Gets all the data from the grid.
61062      * @return {Object} data  data stored in grid
61063      */
61064     getSource : function(){
61065         return this.store.getSource();
61066     }
61067 });/*
61068   
61069  * Licence LGPL
61070  
61071  */
61072  
61073 /**
61074  * @class Roo.grid.Calendar
61075  * @extends Roo.grid.Grid
61076  * This class extends the Grid to provide a calendar widget
61077  * <br><br>Usage:<pre><code>
61078  var grid = new Roo.grid.Calendar("my-container-id", {
61079      ds: myDataStore,
61080      cm: myColModel,
61081      selModel: mySelectionModel,
61082      autoSizeColumns: true,
61083      monitorWindowResize: false,
61084      trackMouseOver: true
61085      eventstore : real data store..
61086  });
61087  // set any options
61088  grid.render();
61089   
61090   * @constructor
61091  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61092  * The container MUST have some type of size defined for the grid to fill. The container will be
61093  * automatically set to position relative if it isn't already.
61094  * @param {Object} config A config object that sets properties on this grid.
61095  */
61096 Roo.grid.Calendar = function(container, config){
61097         // initialize the container
61098         this.container = Roo.get(container);
61099         this.container.update("");
61100         this.container.setStyle("overflow", "hidden");
61101     this.container.addClass('x-grid-container');
61102
61103     this.id = this.container.id;
61104
61105     Roo.apply(this, config);
61106     // check and correct shorthanded configs
61107     
61108     var rows = [];
61109     var d =1;
61110     for (var r = 0;r < 6;r++) {
61111         
61112         rows[r]=[];
61113         for (var c =0;c < 7;c++) {
61114             rows[r][c]= '';
61115         }
61116     }
61117     if (this.eventStore) {
61118         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61119         this.eventStore.on('load',this.onLoad, this);
61120         this.eventStore.on('beforeload',this.clearEvents, this);
61121          
61122     }
61123     
61124     this.dataSource = new Roo.data.Store({
61125             proxy: new Roo.data.MemoryProxy(rows),
61126             reader: new Roo.data.ArrayReader({}, [
61127                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61128     });
61129
61130     this.dataSource.load();
61131     this.ds = this.dataSource;
61132     this.ds.xmodule = this.xmodule || false;
61133     
61134     
61135     var cellRender = function(v,x,r)
61136     {
61137         return String.format(
61138             '<div class="fc-day  fc-widget-content"><div>' +
61139                 '<div class="fc-event-container"></div>' +
61140                 '<div class="fc-day-number">{0}</div>'+
61141                 
61142                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61143             '</div></div>', v);
61144     
61145     }
61146     
61147     
61148     this.colModel = new Roo.grid.ColumnModel( [
61149         {
61150             xtype: 'ColumnModel',
61151             xns: Roo.grid,
61152             dataIndex : 'weekday0',
61153             header : 'Sunday',
61154             renderer : cellRender
61155         },
61156         {
61157             xtype: 'ColumnModel',
61158             xns: Roo.grid,
61159             dataIndex : 'weekday1',
61160             header : 'Monday',
61161             renderer : cellRender
61162         },
61163         {
61164             xtype: 'ColumnModel',
61165             xns: Roo.grid,
61166             dataIndex : 'weekday2',
61167             header : 'Tuesday',
61168             renderer : cellRender
61169         },
61170         {
61171             xtype: 'ColumnModel',
61172             xns: Roo.grid,
61173             dataIndex : 'weekday3',
61174             header : 'Wednesday',
61175             renderer : cellRender
61176         },
61177         {
61178             xtype: 'ColumnModel',
61179             xns: Roo.grid,
61180             dataIndex : 'weekday4',
61181             header : 'Thursday',
61182             renderer : cellRender
61183         },
61184         {
61185             xtype: 'ColumnModel',
61186             xns: Roo.grid,
61187             dataIndex : 'weekday5',
61188             header : 'Friday',
61189             renderer : cellRender
61190         },
61191         {
61192             xtype: 'ColumnModel',
61193             xns: Roo.grid,
61194             dataIndex : 'weekday6',
61195             header : 'Saturday',
61196             renderer : cellRender
61197         }
61198     ]);
61199     this.cm = this.colModel;
61200     this.cm.xmodule = this.xmodule || false;
61201  
61202         
61203           
61204     //this.selModel = new Roo.grid.CellSelectionModel();
61205     //this.sm = this.selModel;
61206     //this.selModel.init(this);
61207     
61208     
61209     if(this.width){
61210         this.container.setWidth(this.width);
61211     }
61212
61213     if(this.height){
61214         this.container.setHeight(this.height);
61215     }
61216     /** @private */
61217         this.addEvents({
61218         // raw events
61219         /**
61220          * @event click
61221          * The raw click event for the entire grid.
61222          * @param {Roo.EventObject} e
61223          */
61224         "click" : true,
61225         /**
61226          * @event dblclick
61227          * The raw dblclick event for the entire grid.
61228          * @param {Roo.EventObject} e
61229          */
61230         "dblclick" : true,
61231         /**
61232          * @event contextmenu
61233          * The raw contextmenu event for the entire grid.
61234          * @param {Roo.EventObject} e
61235          */
61236         "contextmenu" : true,
61237         /**
61238          * @event mousedown
61239          * The raw mousedown event for the entire grid.
61240          * @param {Roo.EventObject} e
61241          */
61242         "mousedown" : true,
61243         /**
61244          * @event mouseup
61245          * The raw mouseup event for the entire grid.
61246          * @param {Roo.EventObject} e
61247          */
61248         "mouseup" : true,
61249         /**
61250          * @event mouseover
61251          * The raw mouseover event for the entire grid.
61252          * @param {Roo.EventObject} e
61253          */
61254         "mouseover" : true,
61255         /**
61256          * @event mouseout
61257          * The raw mouseout event for the entire grid.
61258          * @param {Roo.EventObject} e
61259          */
61260         "mouseout" : true,
61261         /**
61262          * @event keypress
61263          * The raw keypress event for the entire grid.
61264          * @param {Roo.EventObject} e
61265          */
61266         "keypress" : true,
61267         /**
61268          * @event keydown
61269          * The raw keydown event for the entire grid.
61270          * @param {Roo.EventObject} e
61271          */
61272         "keydown" : true,
61273
61274         // custom events
61275
61276         /**
61277          * @event cellclick
61278          * Fires when a cell is clicked
61279          * @param {Grid} this
61280          * @param {Number} rowIndex
61281          * @param {Number} columnIndex
61282          * @param {Roo.EventObject} e
61283          */
61284         "cellclick" : true,
61285         /**
61286          * @event celldblclick
61287          * Fires when a cell is double clicked
61288          * @param {Grid} this
61289          * @param {Number} rowIndex
61290          * @param {Number} columnIndex
61291          * @param {Roo.EventObject} e
61292          */
61293         "celldblclick" : true,
61294         /**
61295          * @event rowclick
61296          * Fires when a row is clicked
61297          * @param {Grid} this
61298          * @param {Number} rowIndex
61299          * @param {Roo.EventObject} e
61300          */
61301         "rowclick" : true,
61302         /**
61303          * @event rowdblclick
61304          * Fires when a row is double clicked
61305          * @param {Grid} this
61306          * @param {Number} rowIndex
61307          * @param {Roo.EventObject} e
61308          */
61309         "rowdblclick" : true,
61310         /**
61311          * @event headerclick
61312          * Fires when a header is clicked
61313          * @param {Grid} this
61314          * @param {Number} columnIndex
61315          * @param {Roo.EventObject} e
61316          */
61317         "headerclick" : true,
61318         /**
61319          * @event headerdblclick
61320          * Fires when a header cell is double clicked
61321          * @param {Grid} this
61322          * @param {Number} columnIndex
61323          * @param {Roo.EventObject} e
61324          */
61325         "headerdblclick" : true,
61326         /**
61327          * @event rowcontextmenu
61328          * Fires when a row is right clicked
61329          * @param {Grid} this
61330          * @param {Number} rowIndex
61331          * @param {Roo.EventObject} e
61332          */
61333         "rowcontextmenu" : true,
61334         /**
61335          * @event cellcontextmenu
61336          * Fires when a cell is right clicked
61337          * @param {Grid} this
61338          * @param {Number} rowIndex
61339          * @param {Number} cellIndex
61340          * @param {Roo.EventObject} e
61341          */
61342          "cellcontextmenu" : true,
61343         /**
61344          * @event headercontextmenu
61345          * Fires when a header is right clicked
61346          * @param {Grid} this
61347          * @param {Number} columnIndex
61348          * @param {Roo.EventObject} e
61349          */
61350         "headercontextmenu" : true,
61351         /**
61352          * @event bodyscroll
61353          * Fires when the body element is scrolled
61354          * @param {Number} scrollLeft
61355          * @param {Number} scrollTop
61356          */
61357         "bodyscroll" : true,
61358         /**
61359          * @event columnresize
61360          * Fires when the user resizes a column
61361          * @param {Number} columnIndex
61362          * @param {Number} newSize
61363          */
61364         "columnresize" : true,
61365         /**
61366          * @event columnmove
61367          * Fires when the user moves a column
61368          * @param {Number} oldIndex
61369          * @param {Number} newIndex
61370          */
61371         "columnmove" : true,
61372         /**
61373          * @event startdrag
61374          * Fires when row(s) start being dragged
61375          * @param {Grid} this
61376          * @param {Roo.GridDD} dd The drag drop object
61377          * @param {event} e The raw browser event
61378          */
61379         "startdrag" : true,
61380         /**
61381          * @event enddrag
61382          * Fires when a drag operation is complete
61383          * @param {Grid} this
61384          * @param {Roo.GridDD} dd The drag drop object
61385          * @param {event} e The raw browser event
61386          */
61387         "enddrag" : true,
61388         /**
61389          * @event dragdrop
61390          * Fires when dragged row(s) are dropped on a valid DD target
61391          * @param {Grid} this
61392          * @param {Roo.GridDD} dd The drag drop object
61393          * @param {String} targetId The target drag drop object
61394          * @param {event} e The raw browser event
61395          */
61396         "dragdrop" : true,
61397         /**
61398          * @event dragover
61399          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61400          * @param {Grid} this
61401          * @param {Roo.GridDD} dd The drag drop object
61402          * @param {String} targetId The target drag drop object
61403          * @param {event} e The raw browser event
61404          */
61405         "dragover" : true,
61406         /**
61407          * @event dragenter
61408          *  Fires when the dragged row(s) first cross another DD target while being dragged
61409          * @param {Grid} this
61410          * @param {Roo.GridDD} dd The drag drop object
61411          * @param {String} targetId The target drag drop object
61412          * @param {event} e The raw browser event
61413          */
61414         "dragenter" : true,
61415         /**
61416          * @event dragout
61417          * Fires when the dragged row(s) leave another DD target while being dragged
61418          * @param {Grid} this
61419          * @param {Roo.GridDD} dd The drag drop object
61420          * @param {String} targetId The target drag drop object
61421          * @param {event} e The raw browser event
61422          */
61423         "dragout" : true,
61424         /**
61425          * @event rowclass
61426          * Fires when a row is rendered, so you can change add a style to it.
61427          * @param {GridView} gridview   The grid view
61428          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61429          */
61430         'rowclass' : true,
61431
61432         /**
61433          * @event render
61434          * Fires when the grid is rendered
61435          * @param {Grid} grid
61436          */
61437         'render' : true,
61438             /**
61439              * @event select
61440              * Fires when a date is selected
61441              * @param {DatePicker} this
61442              * @param {Date} date The selected date
61443              */
61444         'select': true,
61445         /**
61446              * @event monthchange
61447              * Fires when the displayed month changes 
61448              * @param {DatePicker} this
61449              * @param {Date} date The selected month
61450              */
61451         'monthchange': true,
61452         /**
61453              * @event evententer
61454              * Fires when mouse over an event
61455              * @param {Calendar} this
61456              * @param {event} Event
61457              */
61458         'evententer': true,
61459         /**
61460              * @event eventleave
61461              * Fires when the mouse leaves an
61462              * @param {Calendar} this
61463              * @param {event}
61464              */
61465         'eventleave': true,
61466         /**
61467              * @event eventclick
61468              * Fires when the mouse click an
61469              * @param {Calendar} this
61470              * @param {event}
61471              */
61472         'eventclick': true,
61473         /**
61474              * @event eventrender
61475              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
61476              * @param {Calendar} this
61477              * @param {data} data to be modified
61478              */
61479         'eventrender': true
61480         
61481     });
61482
61483     Roo.grid.Grid.superclass.constructor.call(this);
61484     this.on('render', function() {
61485         this.view.el.addClass('x-grid-cal'); 
61486         
61487         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
61488
61489     },this);
61490     
61491     if (!Roo.grid.Calendar.style) {
61492         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
61493             
61494             
61495             '.x-grid-cal .x-grid-col' :  {
61496                 height: 'auto !important',
61497                 'vertical-align': 'top'
61498             },
61499             '.x-grid-cal  .fc-event-hori' : {
61500                 height: '14px'
61501             }
61502              
61503             
61504         }, Roo.id());
61505     }
61506
61507     
61508     
61509 };
61510 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
61511     /**
61512      * @cfg {Store} eventStore The store that loads events.
61513      */
61514     eventStore : 25,
61515
61516      
61517     activeDate : false,
61518     startDay : 0,
61519     autoWidth : true,
61520     monitorWindowResize : false,
61521
61522     
61523     resizeColumns : function() {
61524         var col = (this.view.el.getWidth() / 7) - 3;
61525         // loop through cols, and setWidth
61526         for(var i =0 ; i < 7 ; i++){
61527             this.cm.setColumnWidth(i, col);
61528         }
61529     },
61530      setDate :function(date) {
61531         
61532         Roo.log('setDate?');
61533         
61534         this.resizeColumns();
61535         var vd = this.activeDate;
61536         this.activeDate = date;
61537 //        if(vd && this.el){
61538 //            var t = date.getTime();
61539 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
61540 //                Roo.log('using add remove');
61541 //                
61542 //                this.fireEvent('monthchange', this, date);
61543 //                
61544 //                this.cells.removeClass("fc-state-highlight");
61545 //                this.cells.each(function(c){
61546 //                   if(c.dateValue == t){
61547 //                       c.addClass("fc-state-highlight");
61548 //                       setTimeout(function(){
61549 //                            try{c.dom.firstChild.focus();}catch(e){}
61550 //                       }, 50);
61551 //                       return false;
61552 //                   }
61553 //                   return true;
61554 //                });
61555 //                return;
61556 //            }
61557 //        }
61558         
61559         var days = date.getDaysInMonth();
61560         
61561         var firstOfMonth = date.getFirstDateOfMonth();
61562         var startingPos = firstOfMonth.getDay()-this.startDay;
61563         
61564         if(startingPos < this.startDay){
61565             startingPos += 7;
61566         }
61567         
61568         var pm = date.add(Date.MONTH, -1);
61569         var prevStart = pm.getDaysInMonth()-startingPos;
61570 //        
61571         
61572         
61573         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61574         
61575         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
61576         //this.cells.addClassOnOver('fc-state-hover');
61577         
61578         var cells = this.cells.elements;
61579         var textEls = this.textNodes;
61580         
61581         //Roo.each(cells, function(cell){
61582         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
61583         //});
61584         
61585         days += startingPos;
61586
61587         // convert everything to numbers so it's fast
61588         var day = 86400000;
61589         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
61590         //Roo.log(d);
61591         //Roo.log(pm);
61592         //Roo.log(prevStart);
61593         
61594         var today = new Date().clearTime().getTime();
61595         var sel = date.clearTime().getTime();
61596         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
61597         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
61598         var ddMatch = this.disabledDatesRE;
61599         var ddText = this.disabledDatesText;
61600         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
61601         var ddaysText = this.disabledDaysText;
61602         var format = this.format;
61603         
61604         var setCellClass = function(cal, cell){
61605             
61606             //Roo.log('set Cell Class');
61607             cell.title = "";
61608             var t = d.getTime();
61609             
61610             //Roo.log(d);
61611             
61612             
61613             cell.dateValue = t;
61614             if(t == today){
61615                 cell.className += " fc-today";
61616                 cell.className += " fc-state-highlight";
61617                 cell.title = cal.todayText;
61618             }
61619             if(t == sel){
61620                 // disable highlight in other month..
61621                 cell.className += " fc-state-highlight";
61622                 
61623             }
61624             // disabling
61625             if(t < min) {
61626                 //cell.className = " fc-state-disabled";
61627                 cell.title = cal.minText;
61628                 return;
61629             }
61630             if(t > max) {
61631                 //cell.className = " fc-state-disabled";
61632                 cell.title = cal.maxText;
61633                 return;
61634             }
61635             if(ddays){
61636                 if(ddays.indexOf(d.getDay()) != -1){
61637                     // cell.title = ddaysText;
61638                    // cell.className = " fc-state-disabled";
61639                 }
61640             }
61641             if(ddMatch && format){
61642                 var fvalue = d.dateFormat(format);
61643                 if(ddMatch.test(fvalue)){
61644                     cell.title = ddText.replace("%0", fvalue);
61645                    cell.className = " fc-state-disabled";
61646                 }
61647             }
61648             
61649             if (!cell.initialClassName) {
61650                 cell.initialClassName = cell.dom.className;
61651             }
61652             
61653             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
61654         };
61655
61656         var i = 0;
61657         
61658         for(; i < startingPos; i++) {
61659             cells[i].dayName =  (++prevStart);
61660             Roo.log(textEls[i]);
61661             d.setDate(d.getDate()+1);
61662             
61663             //cells[i].className = "fc-past fc-other-month";
61664             setCellClass(this, cells[i]);
61665         }
61666         
61667         var intDay = 0;
61668         
61669         for(; i < days; i++){
61670             intDay = i - startingPos + 1;
61671             cells[i].dayName =  (intDay);
61672             d.setDate(d.getDate()+1);
61673             
61674             cells[i].className = ''; // "x-date-active";
61675             setCellClass(this, cells[i]);
61676         }
61677         var extraDays = 0;
61678         
61679         for(; i < 42; i++) {
61680             //textEls[i].innerHTML = (++extraDays);
61681             
61682             d.setDate(d.getDate()+1);
61683             cells[i].dayName = (++extraDays);
61684             cells[i].className = "fc-future fc-other-month";
61685             setCellClass(this, cells[i]);
61686         }
61687         
61688         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
61689         
61690         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
61691         
61692         // this will cause all the cells to mis
61693         var rows= [];
61694         var i =0;
61695         for (var r = 0;r < 6;r++) {
61696             for (var c =0;c < 7;c++) {
61697                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
61698             }    
61699         }
61700         
61701         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61702         for(i=0;i<cells.length;i++) {
61703             
61704             this.cells.elements[i].dayName = cells[i].dayName ;
61705             this.cells.elements[i].className = cells[i].className;
61706             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
61707             this.cells.elements[i].title = cells[i].title ;
61708             this.cells.elements[i].dateValue = cells[i].dateValue ;
61709         }
61710         
61711         
61712         
61713         
61714         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61715         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61716         
61717         ////if(totalRows != 6){
61718             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61719            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61720        // }
61721         
61722         this.fireEvent('monthchange', this, date);
61723         
61724         
61725     },
61726  /**
61727      * Returns the grid's SelectionModel.
61728      * @return {SelectionModel}
61729      */
61730     getSelectionModel : function(){
61731         if(!this.selModel){
61732             this.selModel = new Roo.grid.CellSelectionModel();
61733         }
61734         return this.selModel;
61735     },
61736
61737     load: function() {
61738         this.eventStore.load()
61739         
61740         
61741         
61742     },
61743     
61744     findCell : function(dt) {
61745         dt = dt.clearTime().getTime();
61746         var ret = false;
61747         this.cells.each(function(c){
61748             //Roo.log("check " +c.dateValue + '?=' + dt);
61749             if(c.dateValue == dt){
61750                 ret = c;
61751                 return false;
61752             }
61753             return true;
61754         });
61755         
61756         return ret;
61757     },
61758     
61759     findCells : function(rec) {
61760         var s = rec.data.start_dt.clone().clearTime().getTime();
61761        // Roo.log(s);
61762         var e= rec.data.end_dt.clone().clearTime().getTime();
61763        // Roo.log(e);
61764         var ret = [];
61765         this.cells.each(function(c){
61766              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61767             
61768             if(c.dateValue > e){
61769                 return ;
61770             }
61771             if(c.dateValue < s){
61772                 return ;
61773             }
61774             ret.push(c);
61775         });
61776         
61777         return ret;    
61778     },
61779     
61780     findBestRow: function(cells)
61781     {
61782         var ret = 0;
61783         
61784         for (var i =0 ; i < cells.length;i++) {
61785             ret  = Math.max(cells[i].rows || 0,ret);
61786         }
61787         return ret;
61788         
61789     },
61790     
61791     
61792     addItem : function(rec)
61793     {
61794         // look for vertical location slot in
61795         var cells = this.findCells(rec);
61796         
61797         rec.row = this.findBestRow(cells);
61798         
61799         // work out the location.
61800         
61801         var crow = false;
61802         var rows = [];
61803         for(var i =0; i < cells.length; i++) {
61804             if (!crow) {
61805                 crow = {
61806                     start : cells[i],
61807                     end :  cells[i]
61808                 };
61809                 continue;
61810             }
61811             if (crow.start.getY() == cells[i].getY()) {
61812                 // on same row.
61813                 crow.end = cells[i];
61814                 continue;
61815             }
61816             // different row.
61817             rows.push(crow);
61818             crow = {
61819                 start: cells[i],
61820                 end : cells[i]
61821             };
61822             
61823         }
61824         
61825         rows.push(crow);
61826         rec.els = [];
61827         rec.rows = rows;
61828         rec.cells = cells;
61829         for (var i = 0; i < cells.length;i++) {
61830             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61831             
61832         }
61833         
61834         
61835     },
61836     
61837     clearEvents: function() {
61838         
61839         if (!this.eventStore.getCount()) {
61840             return;
61841         }
61842         // reset number of rows in cells.
61843         Roo.each(this.cells.elements, function(c){
61844             c.rows = 0;
61845         });
61846         
61847         this.eventStore.each(function(e) {
61848             this.clearEvent(e);
61849         },this);
61850         
61851     },
61852     
61853     clearEvent : function(ev)
61854     {
61855         if (ev.els) {
61856             Roo.each(ev.els, function(el) {
61857                 el.un('mouseenter' ,this.onEventEnter, this);
61858                 el.un('mouseleave' ,this.onEventLeave, this);
61859                 el.remove();
61860             },this);
61861             ev.els = [];
61862         }
61863     },
61864     
61865     
61866     renderEvent : function(ev,ctr) {
61867         if (!ctr) {
61868              ctr = this.view.el.select('.fc-event-container',true).first();
61869         }
61870         
61871          
61872         this.clearEvent(ev);
61873             //code
61874        
61875         
61876         
61877         ev.els = [];
61878         var cells = ev.cells;
61879         var rows = ev.rows;
61880         this.fireEvent('eventrender', this, ev);
61881         
61882         for(var i =0; i < rows.length; i++) {
61883             
61884             cls = '';
61885             if (i == 0) {
61886                 cls += ' fc-event-start';
61887             }
61888             if ((i+1) == rows.length) {
61889                 cls += ' fc-event-end';
61890             }
61891             
61892             //Roo.log(ev.data);
61893             // how many rows should it span..
61894             var cg = this.eventTmpl.append(ctr,Roo.apply({
61895                 fccls : cls
61896                 
61897             }, ev.data) , true);
61898             
61899             
61900             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61901             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61902             cg.on('click', this.onEventClick, this, ev);
61903             
61904             ev.els.push(cg);
61905             
61906             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61907             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61908             //Roo.log(cg);
61909              
61910             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61911             cg.setWidth(ebox.right - sbox.x -2);
61912         }
61913     },
61914     
61915     renderEvents: function()
61916     {   
61917         // first make sure there is enough space..
61918         
61919         if (!this.eventTmpl) {
61920             this.eventTmpl = new Roo.Template(
61921                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61922                     '<div class="fc-event-inner">' +
61923                         '<span class="fc-event-time">{time}</span>' +
61924                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61925                     '</div>' +
61926                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61927                 '</div>'
61928             );
61929                 
61930         }
61931                
61932         
61933         
61934         this.cells.each(function(c) {
61935             //Roo.log(c.select('.fc-day-content div',true).first());
61936             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61937         });
61938         
61939         var ctr = this.view.el.select('.fc-event-container',true).first();
61940         
61941         var cls;
61942         this.eventStore.each(function(ev){
61943             
61944             this.renderEvent(ev);
61945              
61946              
61947         }, this);
61948         this.view.layout();
61949         
61950     },
61951     
61952     onEventEnter: function (e, el,event,d) {
61953         this.fireEvent('evententer', this, el, event);
61954     },
61955     
61956     onEventLeave: function (e, el,event,d) {
61957         this.fireEvent('eventleave', this, el, event);
61958     },
61959     
61960     onEventClick: function (e, el,event,d) {
61961         this.fireEvent('eventclick', this, el, event);
61962     },
61963     
61964     onMonthChange: function () {
61965         this.store.load();
61966     },
61967     
61968     onLoad: function () {
61969         
61970         //Roo.log('calendar onload');
61971 //         
61972         if(this.eventStore.getCount() > 0){
61973             
61974            
61975             
61976             this.eventStore.each(function(d){
61977                 
61978                 
61979                 // FIXME..
61980                 var add =   d.data;
61981                 if (typeof(add.end_dt) == 'undefined')  {
61982                     Roo.log("Missing End time in calendar data: ");
61983                     Roo.log(d);
61984                     return;
61985                 }
61986                 if (typeof(add.start_dt) == 'undefined')  {
61987                     Roo.log("Missing Start time in calendar data: ");
61988                     Roo.log(d);
61989                     return;
61990                 }
61991                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61992                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61993                 add.id = add.id || d.id;
61994                 add.title = add.title || '??';
61995                 
61996                 this.addItem(d);
61997                 
61998              
61999             },this);
62000         }
62001         
62002         this.renderEvents();
62003     }
62004     
62005
62006 });
62007 /*
62008  grid : {
62009                 xtype: 'Grid',
62010                 xns: Roo.grid,
62011                 listeners : {
62012                     render : function ()
62013                     {
62014                         _this.grid = this;
62015                         
62016                         if (!this.view.el.hasClass('course-timesheet')) {
62017                             this.view.el.addClass('course-timesheet');
62018                         }
62019                         if (this.tsStyle) {
62020                             this.ds.load({});
62021                             return; 
62022                         }
62023                         Roo.log('width');
62024                         Roo.log(_this.grid.view.el.getWidth());
62025                         
62026                         
62027                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62028                             '.course-timesheet .x-grid-row' : {
62029                                 height: '80px'
62030                             },
62031                             '.x-grid-row td' : {
62032                                 'vertical-align' : 0
62033                             },
62034                             '.course-edit-link' : {
62035                                 'color' : 'blue',
62036                                 'text-overflow' : 'ellipsis',
62037                                 'overflow' : 'hidden',
62038                                 'white-space' : 'nowrap',
62039                                 'cursor' : 'pointer'
62040                             },
62041                             '.sub-link' : {
62042                                 'color' : 'green'
62043                             },
62044                             '.de-act-sup-link' : {
62045                                 'color' : 'purple',
62046                                 'text-decoration' : 'line-through'
62047                             },
62048                             '.de-act-link' : {
62049                                 'color' : 'red',
62050                                 'text-decoration' : 'line-through'
62051                             },
62052                             '.course-timesheet .course-highlight' : {
62053                                 'border-top-style': 'dashed !important',
62054                                 'border-bottom-bottom': 'dashed !important'
62055                             },
62056                             '.course-timesheet .course-item' : {
62057                                 'font-family'   : 'tahoma, arial, helvetica',
62058                                 'font-size'     : '11px',
62059                                 'overflow'      : 'hidden',
62060                                 'padding-left'  : '10px',
62061                                 'padding-right' : '10px',
62062                                 'padding-top' : '10px' 
62063                             }
62064                             
62065                         }, Roo.id());
62066                                 this.ds.load({});
62067                     }
62068                 },
62069                 autoWidth : true,
62070                 monitorWindowResize : false,
62071                 cellrenderer : function(v,x,r)
62072                 {
62073                     return v;
62074                 },
62075                 sm : {
62076                     xtype: 'CellSelectionModel',
62077                     xns: Roo.grid
62078                 },
62079                 dataSource : {
62080                     xtype: 'Store',
62081                     xns: Roo.data,
62082                     listeners : {
62083                         beforeload : function (_self, options)
62084                         {
62085                             options.params = options.params || {};
62086                             options.params._month = _this.monthField.getValue();
62087                             options.params.limit = 9999;
62088                             options.params['sort'] = 'when_dt';    
62089                             options.params['dir'] = 'ASC';    
62090                             this.proxy.loadResponse = this.loadResponse;
62091                             Roo.log("load?");
62092                             //this.addColumns();
62093                         },
62094                         load : function (_self, records, options)
62095                         {
62096                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62097                                 // if you click on the translation.. you can edit it...
62098                                 var el = Roo.get(this);
62099                                 var id = el.dom.getAttribute('data-id');
62100                                 var d = el.dom.getAttribute('data-date');
62101                                 var t = el.dom.getAttribute('data-time');
62102                                 //var id = this.child('span').dom.textContent;
62103                                 
62104                                 //Roo.log(this);
62105                                 Pman.Dialog.CourseCalendar.show({
62106                                     id : id,
62107                                     when_d : d,
62108                                     when_t : t,
62109                                     productitem_active : id ? 1 : 0
62110                                 }, function() {
62111                                     _this.grid.ds.load({});
62112                                 });
62113                            
62114                            });
62115                            
62116                            _this.panel.fireEvent('resize', [ '', '' ]);
62117                         }
62118                     },
62119                     loadResponse : function(o, success, response){
62120                             // this is overridden on before load..
62121                             
62122                             Roo.log("our code?");       
62123                             //Roo.log(success);
62124                             //Roo.log(response)
62125                             delete this.activeRequest;
62126                             if(!success){
62127                                 this.fireEvent("loadexception", this, o, response);
62128                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62129                                 return;
62130                             }
62131                             var result;
62132                             try {
62133                                 result = o.reader.read(response);
62134                             }catch(e){
62135                                 Roo.log("load exception?");
62136                                 this.fireEvent("loadexception", this, o, response, e);
62137                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62138                                 return;
62139                             }
62140                             Roo.log("ready...");        
62141                             // loop through result.records;
62142                             // and set this.tdate[date] = [] << array of records..
62143                             _this.tdata  = {};
62144                             Roo.each(result.records, function(r){
62145                                 //Roo.log(r.data);
62146                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62147                                     _this.tdata[r.data.when_dt.format('j')] = [];
62148                                 }
62149                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62150                             });
62151                             
62152                             //Roo.log(_this.tdata);
62153                             
62154                             result.records = [];
62155                             result.totalRecords = 6;
62156                     
62157                             // let's generate some duumy records for the rows.
62158                             //var st = _this.dateField.getValue();
62159                             
62160                             // work out monday..
62161                             //st = st.add(Date.DAY, -1 * st.format('w'));
62162                             
62163                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62164                             
62165                             var firstOfMonth = date.getFirstDayOfMonth();
62166                             var days = date.getDaysInMonth();
62167                             var d = 1;
62168                             var firstAdded = false;
62169                             for (var i = 0; i < result.totalRecords ; i++) {
62170                                 //var d= st.add(Date.DAY, i);
62171                                 var row = {};
62172                                 var added = 0;
62173                                 for(var w = 0 ; w < 7 ; w++){
62174                                     if(!firstAdded && firstOfMonth != w){
62175                                         continue;
62176                                     }
62177                                     if(d > days){
62178                                         continue;
62179                                     }
62180                                     firstAdded = true;
62181                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62182                                     row['weekday'+w] = String.format(
62183                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62184                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62185                                                     d,
62186                                                     date.format('Y-m-')+dd
62187                                                 );
62188                                     added++;
62189                                     if(typeof(_this.tdata[d]) != 'undefined'){
62190                                         Roo.each(_this.tdata[d], function(r){
62191                                             var is_sub = '';
62192                                             var deactive = '';
62193                                             var id = r.id;
62194                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62195                                             if(r.parent_id*1>0){
62196                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62197                                                 id = r.parent_id;
62198                                             }
62199                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62200                                                 deactive = 'de-act-link';
62201                                             }
62202                                             
62203                                             row['weekday'+w] += String.format(
62204                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62205                                                     id, //0
62206                                                     r.product_id_name, //1
62207                                                     r.when_dt.format('h:ia'), //2
62208                                                     is_sub, //3
62209                                                     deactive, //4
62210                                                     desc // 5
62211                                             );
62212                                         });
62213                                     }
62214                                     d++;
62215                                 }
62216                                 
62217                                 // only do this if something added..
62218                                 if(added > 0){ 
62219                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62220                                 }
62221                                 
62222                                 
62223                                 // push it twice. (second one with an hour..
62224                                 
62225                             }
62226                             //Roo.log(result);
62227                             this.fireEvent("load", this, o, o.request.arg);
62228                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62229                         },
62230                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62231                     proxy : {
62232                         xtype: 'HttpProxy',
62233                         xns: Roo.data,
62234                         method : 'GET',
62235                         url : baseURL + '/Roo/Shop_course.php'
62236                     },
62237                     reader : {
62238                         xtype: 'JsonReader',
62239                         xns: Roo.data,
62240                         id : 'id',
62241                         fields : [
62242                             {
62243                                 'name': 'id',
62244                                 'type': 'int'
62245                             },
62246                             {
62247                                 'name': 'when_dt',
62248                                 'type': 'string'
62249                             },
62250                             {
62251                                 'name': 'end_dt',
62252                                 'type': 'string'
62253                             },
62254                             {
62255                                 'name': 'parent_id',
62256                                 'type': 'int'
62257                             },
62258                             {
62259                                 'name': 'product_id',
62260                                 'type': 'int'
62261                             },
62262                             {
62263                                 'name': 'productitem_id',
62264                                 'type': 'int'
62265                             },
62266                             {
62267                                 'name': 'guid',
62268                                 'type': 'int'
62269                             }
62270                         ]
62271                     }
62272                 },
62273                 toolbar : {
62274                     xtype: 'Toolbar',
62275                     xns: Roo,
62276                     items : [
62277                         {
62278                             xtype: 'Button',
62279                             xns: Roo.Toolbar,
62280                             listeners : {
62281                                 click : function (_self, e)
62282                                 {
62283                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62284                                     sd.setMonth(sd.getMonth()-1);
62285                                     _this.monthField.setValue(sd.format('Y-m-d'));
62286                                     _this.grid.ds.load({});
62287                                 }
62288                             },
62289                             text : "Back"
62290                         },
62291                         {
62292                             xtype: 'Separator',
62293                             xns: Roo.Toolbar
62294                         },
62295                         {
62296                             xtype: 'MonthField',
62297                             xns: Roo.form,
62298                             listeners : {
62299                                 render : function (_self)
62300                                 {
62301                                     _this.monthField = _self;
62302                                    // _this.monthField.set  today
62303                                 },
62304                                 select : function (combo, date)
62305                                 {
62306                                     _this.grid.ds.load({});
62307                                 }
62308                             },
62309                             value : (function() { return new Date(); })()
62310                         },
62311                         {
62312                             xtype: 'Separator',
62313                             xns: Roo.Toolbar
62314                         },
62315                         {
62316                             xtype: 'TextItem',
62317                             xns: Roo.Toolbar,
62318                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62319                         },
62320                         {
62321                             xtype: 'Fill',
62322                             xns: Roo.Toolbar
62323                         },
62324                         {
62325                             xtype: 'Button',
62326                             xns: Roo.Toolbar,
62327                             listeners : {
62328                                 click : function (_self, e)
62329                                 {
62330                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62331                                     sd.setMonth(sd.getMonth()+1);
62332                                     _this.monthField.setValue(sd.format('Y-m-d'));
62333                                     _this.grid.ds.load({});
62334                                 }
62335                             },
62336                             text : "Next"
62337                         }
62338                     ]
62339                 },
62340                  
62341             }
62342         };
62343         
62344         *//*
62345  * Based on:
62346  * Ext JS Library 1.1.1
62347  * Copyright(c) 2006-2007, Ext JS, LLC.
62348  *
62349  * Originally Released Under LGPL - original licence link has changed is not relivant.
62350  *
62351  * Fork - LGPL
62352  * <script type="text/javascript">
62353  */
62354  
62355 /**
62356  * @class Roo.LoadMask
62357  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62358  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62359  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62360  * element's UpdateManager load indicator and will be destroyed after the initial load.
62361  * @constructor
62362  * Create a new LoadMask
62363  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62364  * @param {Object} config The config object
62365  */
62366 Roo.LoadMask = function(el, config){
62367     this.el = Roo.get(el);
62368     Roo.apply(this, config);
62369     if(this.store){
62370         this.store.on('beforeload', this.onBeforeLoad, this);
62371         this.store.on('load', this.onLoad, this);
62372         this.store.on('loadexception', this.onLoadException, this);
62373         this.removeMask = false;
62374     }else{
62375         var um = this.el.getUpdateManager();
62376         um.showLoadIndicator = false; // disable the default indicator
62377         um.on('beforeupdate', this.onBeforeLoad, this);
62378         um.on('update', this.onLoad, this);
62379         um.on('failure', this.onLoad, this);
62380         this.removeMask = true;
62381     }
62382 };
62383
62384 Roo.LoadMask.prototype = {
62385     /**
62386      * @cfg {Boolean} removeMask
62387      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62388      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62389      */
62390     removeMask : false,
62391     /**
62392      * @cfg {String} msg
62393      * The text to display in a centered loading message box (defaults to 'Loading...')
62394      */
62395     msg : 'Loading...',
62396     /**
62397      * @cfg {String} msgCls
62398      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62399      */
62400     msgCls : 'x-mask-loading',
62401
62402     /**
62403      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62404      * @type Boolean
62405      */
62406     disabled: false,
62407
62408     /**
62409      * Disables the mask to prevent it from being displayed
62410      */
62411     disable : function(){
62412        this.disabled = true;
62413     },
62414
62415     /**
62416      * Enables the mask so that it can be displayed
62417      */
62418     enable : function(){
62419         this.disabled = false;
62420     },
62421     
62422     onLoadException : function()
62423     {
62424         Roo.log(arguments);
62425         
62426         if (typeof(arguments[3]) != 'undefined') {
62427             Roo.MessageBox.alert("Error loading",arguments[3]);
62428         } 
62429         /*
62430         try {
62431             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62432                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62433             }   
62434         } catch(e) {
62435             
62436         }
62437         */
62438     
62439         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62440     },
62441     // private
62442     onLoad : function()
62443     {
62444         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62445     },
62446
62447     // private
62448     onBeforeLoad : function(){
62449         if(!this.disabled){
62450             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
62451         }
62452     },
62453
62454     // private
62455     destroy : function(){
62456         if(this.store){
62457             this.store.un('beforeload', this.onBeforeLoad, this);
62458             this.store.un('load', this.onLoad, this);
62459             this.store.un('loadexception', this.onLoadException, this);
62460         }else{
62461             var um = this.el.getUpdateManager();
62462             um.un('beforeupdate', this.onBeforeLoad, this);
62463             um.un('update', this.onLoad, this);
62464             um.un('failure', this.onLoad, this);
62465         }
62466     }
62467 };/*
62468  * Based on:
62469  * Ext JS Library 1.1.1
62470  * Copyright(c) 2006-2007, Ext JS, LLC.
62471  *
62472  * Originally Released Under LGPL - original licence link has changed is not relivant.
62473  *
62474  * Fork - LGPL
62475  * <script type="text/javascript">
62476  */
62477
62478
62479 /**
62480  * @class Roo.XTemplate
62481  * @extends Roo.Template
62482  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
62483 <pre><code>
62484 var t = new Roo.XTemplate(
62485         '&lt;select name="{name}"&gt;',
62486                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
62487         '&lt;/select&gt;'
62488 );
62489  
62490 // then append, applying the master template values
62491  </code></pre>
62492  *
62493  * Supported features:
62494  *
62495  *  Tags:
62496
62497 <pre><code>
62498       {a_variable} - output encoded.
62499       {a_variable.format:("Y-m-d")} - call a method on the variable
62500       {a_variable:raw} - unencoded output
62501       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
62502       {a_variable:this.method_on_template(...)} - call a method on the template object.
62503  
62504 </code></pre>
62505  *  The tpl tag:
62506 <pre><code>
62507         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
62508         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
62509         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
62510         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
62511   
62512         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
62513         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
62514 </code></pre>
62515  *      
62516  */
62517 Roo.XTemplate = function()
62518 {
62519     Roo.XTemplate.superclass.constructor.apply(this, arguments);
62520     if (this.html) {
62521         this.compile();
62522     }
62523 };
62524
62525
62526 Roo.extend(Roo.XTemplate, Roo.Template, {
62527
62528     /**
62529      * The various sub templates
62530      */
62531     tpls : false,
62532     /**
62533      *
62534      * basic tag replacing syntax
62535      * WORD:WORD()
62536      *
62537      * // you can fake an object call by doing this
62538      *  x.t:(test,tesT) 
62539      * 
62540      */
62541     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
62542
62543     /**
62544      * compile the template
62545      *
62546      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
62547      *
62548      */
62549     compile: function()
62550     {
62551         var s = this.html;
62552      
62553         s = ['<tpl>', s, '</tpl>'].join('');
62554     
62555         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
62556             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
62557             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
62558             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
62559             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
62560             m,
62561             id     = 0,
62562             tpls   = [];
62563     
62564         while(true == !!(m = s.match(re))){
62565             var forMatch   = m[0].match(nameRe),
62566                 ifMatch   = m[0].match(ifRe),
62567                 execMatch   = m[0].match(execRe),
62568                 namedMatch   = m[0].match(namedRe),
62569                 
62570                 exp  = null, 
62571                 fn   = null,
62572                 exec = null,
62573                 name = forMatch && forMatch[1] ? forMatch[1] : '';
62574                 
62575             if (ifMatch) {
62576                 // if - puts fn into test..
62577                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
62578                 if(exp){
62579                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
62580                 }
62581             }
62582             
62583             if (execMatch) {
62584                 // exec - calls a function... returns empty if true is  returned.
62585                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
62586                 if(exp){
62587                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
62588                 }
62589             }
62590             
62591             
62592             if (name) {
62593                 // for = 
62594                 switch(name){
62595                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
62596                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
62597                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
62598                 }
62599             }
62600             var uid = namedMatch ? namedMatch[1] : id;
62601             
62602             
62603             tpls.push({
62604                 id:     namedMatch ? namedMatch[1] : id,
62605                 target: name,
62606                 exec:   exec,
62607                 test:   fn,
62608                 body:   m[1] || ''
62609             });
62610             if (namedMatch) {
62611                 s = s.replace(m[0], '');
62612             } else { 
62613                 s = s.replace(m[0], '{xtpl'+ id + '}');
62614             }
62615             ++id;
62616         }
62617         this.tpls = [];
62618         for(var i = tpls.length-1; i >= 0; --i){
62619             this.compileTpl(tpls[i]);
62620             this.tpls[tpls[i].id] = tpls[i];
62621         }
62622         this.master = tpls[tpls.length-1];
62623         return this;
62624     },
62625     /**
62626      * same as applyTemplate, except it's done to one of the subTemplates
62627      * when using named templates, you can do:
62628      *
62629      * var str = pl.applySubTemplate('your-name', values);
62630      *
62631      * 
62632      * @param {Number} id of the template
62633      * @param {Object} values to apply to template
62634      * @param {Object} parent (normaly the instance of this object)
62635      */
62636     applySubTemplate : function(id, values, parent)
62637     {
62638         
62639         
62640         var t = this.tpls[id];
62641         
62642         
62643         try { 
62644             if(t.test && !t.test.call(this, values, parent)){
62645                 return '';
62646             }
62647         } catch(e) {
62648             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
62649             Roo.log(e.toString());
62650             Roo.log(t.test);
62651             return ''
62652         }
62653         try { 
62654             
62655             if(t.exec && t.exec.call(this, values, parent)){
62656                 return '';
62657             }
62658         } catch(e) {
62659             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
62660             Roo.log(e.toString());
62661             Roo.log(t.exec);
62662             return ''
62663         }
62664         try {
62665             var vs = t.target ? t.target.call(this, values, parent) : values;
62666             parent = t.target ? values : parent;
62667             if(t.target && vs instanceof Array){
62668                 var buf = [];
62669                 for(var i = 0, len = vs.length; i < len; i++){
62670                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
62671                 }
62672                 return buf.join('');
62673             }
62674             return t.compiled.call(this, vs, parent);
62675         } catch (e) {
62676             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
62677             Roo.log(e.toString());
62678             Roo.log(t.compiled);
62679             return '';
62680         }
62681     },
62682
62683     compileTpl : function(tpl)
62684     {
62685         var fm = Roo.util.Format;
62686         var useF = this.disableFormats !== true;
62687         var sep = Roo.isGecko ? "+" : ",";
62688         var undef = function(str) {
62689             Roo.log("Property not found :"  + str);
62690             return '';
62691         };
62692         
62693         var fn = function(m, name, format, args)
62694         {
62695             //Roo.log(arguments);
62696             args = args ? args.replace(/\\'/g,"'") : args;
62697             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
62698             if (typeof(format) == 'undefined') {
62699                 format= 'htmlEncode';
62700             }
62701             if (format == 'raw' ) {
62702                 format = false;
62703             }
62704             
62705             if(name.substr(0, 4) == 'xtpl'){
62706                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
62707             }
62708             
62709             // build an array of options to determine if value is undefined..
62710             
62711             // basically get 'xxxx.yyyy' then do
62712             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
62713             //    (function () { Roo.log("Property not found"); return ''; })() :
62714             //    ......
62715             
62716             var udef_ar = [];
62717             var lookfor = '';
62718             Roo.each(name.split('.'), function(st) {
62719                 lookfor += (lookfor.length ? '.': '') + st;
62720                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62721             });
62722             
62723             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62724             
62725             
62726             if(format && useF){
62727                 
62728                 args = args ? ',' + args : "";
62729                  
62730                 if(format.substr(0, 5) != "this."){
62731                     format = "fm." + format + '(';
62732                 }else{
62733                     format = 'this.call("'+ format.substr(5) + '", ';
62734                     args = ", values";
62735                 }
62736                 
62737                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62738             }
62739              
62740             if (args.length) {
62741                 // called with xxyx.yuu:(test,test)
62742                 // change to ()
62743                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62744             }
62745             // raw.. - :raw modifier..
62746             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62747             
62748         };
62749         var body;
62750         // branched to use + in gecko and [].join() in others
62751         if(Roo.isGecko){
62752             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62753                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62754                     "';};};";
62755         }else{
62756             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62757             body.push(tpl.body.replace(/(\r\n|\n)/g,
62758                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62759             body.push("'].join('');};};");
62760             body = body.join('');
62761         }
62762         
62763         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62764        
62765         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62766         eval(body);
62767         
62768         return this;
62769     },
62770
62771     applyTemplate : function(values){
62772         return this.master.compiled.call(this, values, {});
62773         //var s = this.subs;
62774     },
62775
62776     apply : function(){
62777         return this.applyTemplate.apply(this, arguments);
62778     }
62779
62780  });
62781
62782 Roo.XTemplate.from = function(el){
62783     el = Roo.getDom(el);
62784     return new Roo.XTemplate(el.value || el.innerHTML);
62785 };