f903c90a4242a29c4f6985780eace68dd011b17d
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 } 
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957
958 /**
959   * Make the first letter of a string uppercase
960   *
961   * @return {String} The new string.
962   */
963 String.prototype.toUpperCaseFirst = function () {
964     return this.charAt(0).toUpperCase() + this.slice(1);
965 };  
966   
967 /*
968  * Based on:
969  * Ext JS Library 1.1.1
970  * Copyright(c) 2006-2007, Ext JS, LLC.
971  *
972  * Originally Released Under LGPL - original licence link has changed is not relivant.
973  *
974  * Fork - LGPL
975  * <script type="text/javascript">
976  */
977
978  /**
979  * @class Number
980  */
981 Roo.applyIf(Number.prototype, {
982     /**
983      * Checks whether or not the current number is within a desired range.  If the number is already within the
984      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985      * exceeded.  Note that this method returns the constrained value but does not change the current number.
986      * @param {Number} min The minimum number in the range
987      * @param {Number} max The maximum number in the range
988      * @return {Number} The constrained value if outside the range, otherwise the current value
989      */
990     constrain : function(min, max){
991         return Math.min(Math.max(this, min), max);
992     }
993 });/*
994  * Based on:
995  * Ext JS Library 1.1.1
996  * Copyright(c) 2006-2007, Ext JS, LLC.
997  *
998  * Originally Released Under LGPL - original licence link has changed is not relivant.
999  *
1000  * Fork - LGPL
1001  * <script type="text/javascript">
1002  */
1003  /**
1004  * @class Array
1005  */
1006 Roo.applyIf(Array.prototype, {
1007     /**
1008      * 
1009      * Checks whether or not the specified object exists in the array.
1010      * @param {Object} o The object to check for
1011      * @return {Number} The index of o in the array (or -1 if it is not found)
1012      */
1013     indexOf : function(o){
1014        for (var i = 0, len = this.length; i < len; i++){
1015               if(this[i] == o) { return i; }
1016        }
1017            return -1;
1018     },
1019
1020     /**
1021      * Removes the specified object from the array.  If the object is not found nothing happens.
1022      * @param {Object} o The object to remove
1023      */
1024     remove : function(o){
1025        var index = this.indexOf(o);
1026        if(index != -1){
1027            this.splice(index, 1);
1028        }
1029     },
1030     /**
1031      * Map (JS 1.6 compatibility)
1032      * @param {Function} function  to call
1033      */
1034     map : function(fun )
1035     {
1036         var len = this.length >>> 0;
1037         if (typeof fun != "function") {
1038             throw new TypeError();
1039         }
1040         var res = new Array(len);
1041         var thisp = arguments[1];
1042         for (var i = 0; i < len; i++)
1043         {
1044             if (i in this) {
1045                 res[i] = fun.call(thisp, this[i], i, this);
1046             }
1047         }
1048
1049         return res;
1050     },
1051     /**
1052      * equals
1053      * @param {Array} o The array to compare to
1054      * @returns {Boolean} true if the same
1055      */
1056     equals : function(b)
1057     {
1058             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1059         if (this === b) {
1060             return true;
1061         }
1062         if (b == null) {
1063             return false;
1064         }
1065         if (this.length !== b.length) {
1066             return false;
1067         }
1068           
1069         // sort?? a.sort().equals(b.sort());
1070           
1071         for (var i = 0; i < this.length; ++i) {
1072             if (this[i] !== b[i]) {
1073             return false;
1074             }
1075         }
1076         return true;
1077     } 
1078     
1079     
1080     
1081     
1082 });
1083
1084 Roo.applyIf(Array, {
1085  /**
1086      * from
1087      * @static
1088      * @param {Array} o Or Array like object (eg. nodelist)
1089      * @returns {Array} 
1090      */
1091     from : function(o)
1092     {
1093         var ret= [];
1094     
1095         for (var i =0; i < o.length; i++) { 
1096             ret[i] = o[i];
1097         }
1098         return ret;
1099       
1100     }
1101 });
1102 /*
1103  * Based on:
1104  * Ext JS Library 1.1.1
1105  * Copyright(c) 2006-2007, Ext JS, LLC.
1106  *
1107  * Originally Released Under LGPL - original licence link has changed is not relivant.
1108  *
1109  * Fork - LGPL
1110  * <script type="text/javascript">
1111  */
1112
1113 /**
1114  * @class Date
1115  *
1116  * The date parsing and format syntax is a subset of
1117  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118  * supported will provide results equivalent to their PHP versions.
1119  *
1120  * Following is the list of all currently supported formats:
1121  *<pre>
1122 Sample date:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1124
1125 Format  Output      Description
1126 ------  ----------  --------------------------------------------------------------
1127   d      10         Day of the month, 2 digits with leading zeros
1128   D      Wed        A textual representation of a day, three letters
1129   j      10         Day of the month without leading zeros
1130   l      Wednesday  A full textual representation of the day of the week
1131   S      th         English ordinal day of month suffix, 2 chars (use with j)
1132   w      3          Numeric representation of the day of the week
1133   z      9          The julian date, or day of the year (0-365)
1134   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135   F      January    A full textual representation of the month
1136   m      01         Numeric representation of a month, with leading zeros
1137   M      Jan        Month name abbreviation, three letters
1138   n      1          Numeric representation of a month, without leading zeros
1139   t      31         Number of days in the given month
1140   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1141   Y      2007       A full numeric representation of a year, 4 digits
1142   y      07         A two digit representation of a year
1143   a      pm         Lowercase Ante meridiem and Post meridiem
1144   A      PM         Uppercase Ante meridiem and Post meridiem
1145   g      3          12-hour format of an hour without leading zeros
1146   G      15         24-hour format of an hour without leading zeros
1147   h      03         12-hour format of an hour with leading zeros
1148   H      15         24-hour format of an hour with leading zeros
1149   i      05         Minutes with leading zeros
1150   s      01         Seconds, with leading zeros
1151   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1153   T      CST        Timezone setting of the machine running the code
1154   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1155 </pre>
1156  *
1157  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1158  * <pre><code>
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d'));                         //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1162 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1163  </code></pre>
1164  *
1165  * Here are some standard date/time patterns that you might find helpful.  They
1166  * are not part of the source of Date.js, but to use them you can simply copy this
1167  * block of code into any script that is included after Date.js and they will also become
1168  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1169  * <pre><code>
1170 Date.patterns = {
1171     ISO8601Long:"Y-m-d H:i:s",
1172     ISO8601Short:"Y-m-d",
1173     ShortDate: "n/j/Y",
1174     LongDate: "l, F d, Y",
1175     FullDateTime: "l, F d, Y g:i:s A",
1176     MonthDay: "F d",
1177     ShortTime: "g:i A",
1178     LongTime: "g:i:s A",
1179     SortableDateTime: "Y-m-d\\TH:i:s",
1180     UniversalSortableDateTime: "Y-m-d H:i:sO",
1181     YearMonth: "F, Y"
1182 };
1183 </code></pre>
1184  *
1185  * Example usage:
1186  * <pre><code>
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1189  </code></pre>
1190  */
1191
1192 /*
1193  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194  * They generate precompiled functions from date formats instead of parsing and
1195  * processing the pattern every time you format a date.  These functions are available
1196  * on every Date object (any javascript function).
1197  *
1198  * The original article and download are here:
1199  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1200  *
1201  */
1202  
1203  
1204  // was in core
1205 /**
1206  Returns the number of milliseconds between this date and date
1207  @param {Date} date (optional) Defaults to now
1208  @return {Number} The diff in milliseconds
1209  @member Date getElapsed
1210  */
1211 Date.prototype.getElapsed = function(date) {
1212         return Math.abs((date || new Date()).getTime()-this.getTime());
1213 };
1214 // was in date file..
1215
1216
1217 // private
1218 Date.parseFunctions = {count:0};
1219 // private
1220 Date.parseRegexes = [];
1221 // private
1222 Date.formatFunctions = {count:0};
1223
1224 // private
1225 Date.prototype.dateFormat = function(format) {
1226     if (Date.formatFunctions[format] == null) {
1227         Date.createNewFormat(format);
1228     }
1229     var func = Date.formatFunctions[format];
1230     return this[func]();
1231 };
1232
1233
1234 /**
1235  * Formats a date given the supplied format string
1236  * @param {String} format The format string
1237  * @return {String} The formatted date
1238  * @method
1239  */
1240 Date.prototype.format = Date.prototype.dateFormat;
1241
1242 // private
1243 Date.createNewFormat = function(format) {
1244     var funcName = "format" + Date.formatFunctions.count++;
1245     Date.formatFunctions[format] = funcName;
1246     var code = "Date.prototype." + funcName + " = function(){return ";
1247     var special = false;
1248     var ch = '';
1249     for (var i = 0; i < format.length; ++i) {
1250         ch = format.charAt(i);
1251         if (!special && ch == "\\") {
1252             special = true;
1253         }
1254         else if (special) {
1255             special = false;
1256             code += "'" + String.escape(ch) + "' + ";
1257         }
1258         else {
1259             code += Date.getFormatCode(ch);
1260         }
1261     }
1262     /** eval:var:zzzzzzzzzzzzz */
1263     eval(code.substring(0, code.length - 3) + ";}");
1264 };
1265
1266 // private
1267 Date.getFormatCode = function(character) {
1268     switch (character) {
1269     case "d":
1270         return "String.leftPad(this.getDate(), 2, '0') + ";
1271     case "D":
1272         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1273     case "j":
1274         return "this.getDate() + ";
1275     case "l":
1276         return "Date.dayNames[this.getDay()] + ";
1277     case "S":
1278         return "this.getSuffix() + ";
1279     case "w":
1280         return "this.getDay() + ";
1281     case "z":
1282         return "this.getDayOfYear() + ";
1283     case "W":
1284         return "this.getWeekOfYear() + ";
1285     case "F":
1286         return "Date.monthNames[this.getMonth()] + ";
1287     case "m":
1288         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1289     case "M":
1290         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1291     case "n":
1292         return "(this.getMonth() + 1) + ";
1293     case "t":
1294         return "this.getDaysInMonth() + ";
1295     case "L":
1296         return "(this.isLeapYear() ? 1 : 0) + ";
1297     case "Y":
1298         return "this.getFullYear() + ";
1299     case "y":
1300         return "('' + this.getFullYear()).substring(2, 4) + ";
1301     case "a":
1302         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1303     case "A":
1304         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1305     case "g":
1306         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1307     case "G":
1308         return "this.getHours() + ";
1309     case "h":
1310         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1311     case "H":
1312         return "String.leftPad(this.getHours(), 2, '0') + ";
1313     case "i":
1314         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1315     case "s":
1316         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1317     case "O":
1318         return "this.getGMTOffset() + ";
1319     case "P":
1320         return "this.getGMTColonOffset() + ";
1321     case "T":
1322         return "this.getTimezone() + ";
1323     case "Z":
1324         return "(this.getTimezoneOffset() * -60) + ";
1325     default:
1326         return "'" + String.escape(character) + "' + ";
1327     }
1328 };
1329
1330 /**
1331  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1332  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1333  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1334  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1335  * string or the parse operation will fail.
1336  * Example Usage:
1337 <pre><code>
1338 //dt = Fri May 25 2007 (current date)
1339 var dt = new Date();
1340
1341 //dt = Thu May 25 2006 (today's month/day in 2006)
1342 dt = Date.parseDate("2006", "Y");
1343
1344 //dt = Sun Jan 15 2006 (all date parts specified)
1345 dt = Date.parseDate("2006-1-15", "Y-m-d");
1346
1347 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1348 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1349 </code></pre>
1350  * @param {String} input The unparsed date as a string
1351  * @param {String} format The format the date is in
1352  * @return {Date} The parsed date
1353  * @static
1354  */
1355 Date.parseDate = function(input, format) {
1356     if (Date.parseFunctions[format] == null) {
1357         Date.createParser(format);
1358     }
1359     var func = Date.parseFunctions[format];
1360     return Date[func](input);
1361 };
1362 /**
1363  * @private
1364  */
1365
1366 Date.createParser = function(format) {
1367     var funcName = "parse" + Date.parseFunctions.count++;
1368     var regexNum = Date.parseRegexes.length;
1369     var currentGroup = 1;
1370     Date.parseFunctions[format] = funcName;
1371
1372     var code = "Date." + funcName + " = function(input){\n"
1373         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1374         + "var d = new Date();\n"
1375         + "y = d.getFullYear();\n"
1376         + "m = d.getMonth();\n"
1377         + "d = d.getDate();\n"
1378         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1379         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1380         + "if (results && results.length > 0) {";
1381     var regex = "";
1382
1383     var special = false;
1384     var ch = '';
1385     for (var i = 0; i < format.length; ++i) {
1386         ch = format.charAt(i);
1387         if (!special && ch == "\\") {
1388             special = true;
1389         }
1390         else if (special) {
1391             special = false;
1392             regex += String.escape(ch);
1393         }
1394         else {
1395             var obj = Date.formatCodeToRegex(ch, currentGroup);
1396             currentGroup += obj.g;
1397             regex += obj.s;
1398             if (obj.g && obj.c) {
1399                 code += obj.c;
1400             }
1401         }
1402     }
1403
1404     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1405         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1406         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1407         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1408         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1409         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1410         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1411         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1412         + "else if (y >= 0 && m >= 0)\n"
1413         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1414         + "else if (y >= 0)\n"
1415         + "{v = new Date(y); v.setFullYear(y);}\n"
1416         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1417         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1418         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1419         + ";}";
1420
1421     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1422     /** eval:var:zzzzzzzzzzzzz */
1423     eval(code);
1424 };
1425
1426 // private
1427 Date.formatCodeToRegex = function(character, currentGroup) {
1428     switch (character) {
1429     case "D":
1430         return {g:0,
1431         c:null,
1432         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1433     case "j":
1434         return {g:1,
1435             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{1,2})"}; // day of month without leading zeroes
1437     case "d":
1438         return {g:1,
1439             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1440             s:"(\\d{2})"}; // day of month with leading zeroes
1441     case "l":
1442         return {g:0,
1443             c:null,
1444             s:"(?:" + Date.dayNames.join("|") + ")"};
1445     case "S":
1446         return {g:0,
1447             c:null,
1448             s:"(?:st|nd|rd|th)"};
1449     case "w":
1450         return {g:0,
1451             c:null,
1452             s:"\\d"};
1453     case "z":
1454         return {g:0,
1455             c:null,
1456             s:"(?:\\d{1,3})"};
1457     case "W":
1458         return {g:0,
1459             c:null,
1460             s:"(?:\\d{2})"};
1461     case "F":
1462         return {g:1,
1463             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1464             s:"(" + Date.monthNames.join("|") + ")"};
1465     case "M":
1466         return {g:1,
1467             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1468             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1469     case "n":
1470         return {g:1,
1471             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1472             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1473     case "m":
1474         return {g:1,
1475             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1476             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1477     case "t":
1478         return {g:0,
1479             c:null,
1480             s:"\\d{1,2}"};
1481     case "L":
1482         return {g:0,
1483             c:null,
1484             s:"(?:1|0)"};
1485     case "Y":
1486         return {g:1,
1487             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1488             s:"(\\d{4})"};
1489     case "y":
1490         return {g:1,
1491             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1492                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1493             s:"(\\d{1,2})"};
1494     case "a":
1495         return {g:1,
1496             c:"if (results[" + currentGroup + "] == 'am') {\n"
1497                 + "if (h == 12) { h = 0; }\n"
1498                 + "} else { if (h < 12) { h += 12; }}",
1499             s:"(am|pm)"};
1500     case "A":
1501         return {g:1,
1502             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1503                 + "if (h == 12) { h = 0; }\n"
1504                 + "} else { if (h < 12) { h += 12; }}",
1505             s:"(AM|PM)"};
1506     case "g":
1507     case "G":
1508         return {g:1,
1509             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1511     case "h":
1512     case "H":
1513         return {g:1,
1514             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1515             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1516     case "i":
1517         return {g:1,
1518             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1519             s:"(\\d{2})"};
1520     case "s":
1521         return {g:1,
1522             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1523             s:"(\\d{2})"};
1524     case "O":
1525         return {g:1,
1526             c:[
1527                 "o = results[", currentGroup, "];\n",
1528                 "var sn = o.substring(0,1);\n", // get + / - sign
1529                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1530                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1531                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1532                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1533             ].join(""),
1534             s:"([+\-]\\d{2,4})"};
1535     
1536     
1537     case "P":
1538         return {g:1,
1539                 c:[
1540                    "o = results[", currentGroup, "];\n",
1541                    "var sn = o.substring(0,1);\n",
1542                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1543                    "var mn = o.substring(4,6) % 60;\n",
1544                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1545                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1546             ].join(""),
1547             s:"([+\-]\\d{4})"};
1548     case "T":
1549         return {g:0,
1550             c:null,
1551             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1552     case "Z":
1553         return {g:1,
1554             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1555                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1556             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1557     default:
1558         return {g:0,
1559             c:null,
1560             s:String.escape(character)};
1561     }
1562 };
1563
1564 /**
1565  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1566  * @return {String} The abbreviated timezone name (e.g. 'CST')
1567  */
1568 Date.prototype.getTimezone = function() {
1569     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1574  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1575  */
1576 Date.prototype.getGMTOffset = function() {
1577     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1578         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1579         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1580 };
1581
1582 /**
1583  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1584  * @return {String} 2-characters representing hours and 2-characters representing minutes
1585  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1586  */
1587 Date.prototype.getGMTColonOffset = function() {
1588         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1589                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1590                 + ":"
1591                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1592 }
1593
1594 /**
1595  * Get the numeric day number of the year, adjusted for leap year.
1596  * @return {Number} 0 through 364 (365 in leap years)
1597  */
1598 Date.prototype.getDayOfYear = function() {
1599     var num = 0;
1600     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1601     for (var i = 0; i < this.getMonth(); ++i) {
1602         num += Date.daysInMonth[i];
1603     }
1604     return num + this.getDate() - 1;
1605 };
1606
1607 /**
1608  * Get the string representation of the numeric week number of the year
1609  * (equivalent to the format specifier 'W').
1610  * @return {String} '00' through '52'
1611  */
1612 Date.prototype.getWeekOfYear = function() {
1613     // Skip to Thursday of this week
1614     var now = this.getDayOfYear() + (4 - this.getDay());
1615     // Find the first Thursday of the year
1616     var jan1 = new Date(this.getFullYear(), 0, 1);
1617     var then = (7 - jan1.getDay() + 4);
1618     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1619 };
1620
1621 /**
1622  * Whether or not the current date is in a leap year.
1623  * @return {Boolean} True if the current date is in a leap year, else false
1624  */
1625 Date.prototype.isLeapYear = function() {
1626     var year = this.getFullYear();
1627     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1628 };
1629
1630 /**
1631  * Get the first day of the current month, adjusted for leap year.  The returned value
1632  * is the numeric day index within the week (0-6) which can be used in conjunction with
1633  * the {@link #monthNames} array to retrieve the textual day name.
1634  * Example:
1635  *<pre><code>
1636 var dt = new Date('1/10/2007');
1637 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1638 </code></pre>
1639  * @return {Number} The day number (0-6)
1640  */
1641 Date.prototype.getFirstDayOfMonth = function() {
1642     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1643     return (day < 0) ? (day + 7) : day;
1644 };
1645
1646 /**
1647  * Get the last day of the current month, adjusted for leap year.  The returned value
1648  * is the numeric day index within the week (0-6) which can be used in conjunction with
1649  * the {@link #monthNames} array to retrieve the textual day name.
1650  * Example:
1651  *<pre><code>
1652 var dt = new Date('1/10/2007');
1653 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1654 </code></pre>
1655  * @return {Number} The day number (0-6)
1656  */
1657 Date.prototype.getLastDayOfMonth = function() {
1658     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1659     return (day < 0) ? (day + 7) : day;
1660 };
1661
1662
1663 /**
1664  * Get the first date of this date's month
1665  * @return {Date}
1666  */
1667 Date.prototype.getFirstDateOfMonth = function() {
1668     return new Date(this.getFullYear(), this.getMonth(), 1);
1669 };
1670
1671 /**
1672  * Get the last date of this date's month
1673  * @return {Date}
1674  */
1675 Date.prototype.getLastDateOfMonth = function() {
1676     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1677 };
1678 /**
1679  * Get the number of days in the current month, adjusted for leap year.
1680  * @return {Number} The number of days in the month
1681  */
1682 Date.prototype.getDaysInMonth = function() {
1683     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1684     return Date.daysInMonth[this.getMonth()];
1685 };
1686
1687 /**
1688  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1689  * @return {String} 'st, 'nd', 'rd' or 'th'
1690  */
1691 Date.prototype.getSuffix = function() {
1692     switch (this.getDate()) {
1693         case 1:
1694         case 21:
1695         case 31:
1696             return "st";
1697         case 2:
1698         case 22:
1699             return "nd";
1700         case 3:
1701         case 23:
1702             return "rd";
1703         default:
1704             return "th";
1705     }
1706 };
1707
1708 // private
1709 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1710
1711 /**
1712  * An array of textual month names.
1713  * Override these values for international dates, for example...
1714  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1715  * @type Array
1716  * @static
1717  */
1718 Date.monthNames =
1719    ["January",
1720     "February",
1721     "March",
1722     "April",
1723     "May",
1724     "June",
1725     "July",
1726     "August",
1727     "September",
1728     "October",
1729     "November",
1730     "December"];
1731
1732 /**
1733  * An array of textual day names.
1734  * Override these values for international dates, for example...
1735  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1736  * @type Array
1737  * @static
1738  */
1739 Date.dayNames =
1740    ["Sunday",
1741     "Monday",
1742     "Tuesday",
1743     "Wednesday",
1744     "Thursday",
1745     "Friday",
1746     "Saturday"];
1747
1748 // private
1749 Date.y2kYear = 50;
1750 // private
1751 Date.monthNumbers = {
1752     Jan:0,
1753     Feb:1,
1754     Mar:2,
1755     Apr:3,
1756     May:4,
1757     Jun:5,
1758     Jul:6,
1759     Aug:7,
1760     Sep:8,
1761     Oct:9,
1762     Nov:10,
1763     Dec:11};
1764
1765 /**
1766  * Creates and returns a new Date instance with the exact same date value as the called instance.
1767  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1768  * variable will also be changed.  When the intention is to create a new variable that will not
1769  * modify the original instance, you should create a clone.
1770  *
1771  * Example of correctly cloning a date:
1772  * <pre><code>
1773 //wrong way:
1774 var orig = new Date('10/1/2006');
1775 var copy = orig;
1776 copy.setDate(5);
1777 document.write(orig);  //returns 'Thu Oct 05 2006'!
1778
1779 //correct way:
1780 var orig = new Date('10/1/2006');
1781 var copy = orig.clone();
1782 copy.setDate(5);
1783 document.write(orig);  //returns 'Thu Oct 01 2006'
1784 </code></pre>
1785  * @return {Date} The new Date instance
1786  */
1787 Date.prototype.clone = function() {
1788         return new Date(this.getTime());
1789 };
1790
1791 /**
1792  * Clears any time information from this date
1793  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1794  @return {Date} this or the clone
1795  */
1796 Date.prototype.clearTime = function(clone){
1797     if(clone){
1798         return this.clone().clearTime();
1799     }
1800     this.setHours(0);
1801     this.setMinutes(0);
1802     this.setSeconds(0);
1803     this.setMilliseconds(0);
1804     return this;
1805 };
1806
1807 // private
1808 // safari setMonth is broken -- check that this is only donw once...
1809 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1810     Date.brokenSetMonth = Date.prototype.setMonth;
1811         Date.prototype.setMonth = function(num){
1812                 if(num <= -1){
1813                         var n = Math.ceil(-num);
1814                         var back_year = Math.ceil(n/12);
1815                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1816                         this.setFullYear(this.getFullYear() - back_year);
1817                         return Date.brokenSetMonth.call(this, month);
1818                 } else {
1819                         return Date.brokenSetMonth.apply(this, arguments);
1820                 }
1821         };
1822 }
1823
1824 /** Date interval constant 
1825 * @static 
1826 * @type String */
1827 Date.MILLI = "ms";
1828 /** Date interval constant 
1829 * @static 
1830 * @type String */
1831 Date.SECOND = "s";
1832 /** Date interval constant 
1833 * @static 
1834 * @type String */
1835 Date.MINUTE = "mi";
1836 /** Date interval constant 
1837 * @static 
1838 * @type String */
1839 Date.HOUR = "h";
1840 /** Date interval constant 
1841 * @static 
1842 * @type String */
1843 Date.DAY = "d";
1844 /** Date interval constant 
1845 * @static 
1846 * @type String */
1847 Date.MONTH = "mo";
1848 /** Date interval constant 
1849 * @static 
1850 * @type String */
1851 Date.YEAR = "y";
1852
1853 /**
1854  * Provides a convenient method of performing basic date arithmetic.  This method
1855  * does not modify the Date instance being called - it creates and returns
1856  * a new Date instance containing the resulting date value.
1857  *
1858  * Examples:
1859  * <pre><code>
1860 //Basic usage:
1861 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1862 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1863
1864 //Negative values will subtract correctly:
1865 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1866 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1867
1868 //You can even chain several calls together in one line!
1869 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1870 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1871  </code></pre>
1872  *
1873  * @param {String} interval   A valid date interval enum value
1874  * @param {Number} value      The amount to add to the current date
1875  * @return {Date} The new Date instance
1876  */
1877 Date.prototype.add = function(interval, value){
1878   var d = this.clone();
1879   if (!interval || value === 0) { return d; }
1880   switch(interval.toLowerCase()){
1881     case Date.MILLI:
1882       d.setMilliseconds(this.getMilliseconds() + value);
1883       break;
1884     case Date.SECOND:
1885       d.setSeconds(this.getSeconds() + value);
1886       break;
1887     case Date.MINUTE:
1888       d.setMinutes(this.getMinutes() + value);
1889       break;
1890     case Date.HOUR:
1891       d.setHours(this.getHours() + value);
1892       break;
1893     case Date.DAY:
1894       d.setDate(this.getDate() + value);
1895       break;
1896     case Date.MONTH:
1897       var day = this.getDate();
1898       if(day > 28){
1899           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1900       }
1901       d.setDate(day);
1902       d.setMonth(this.getMonth() + value);
1903       break;
1904     case Date.YEAR:
1905       d.setFullYear(this.getFullYear() + value);
1906       break;
1907   }
1908   return d;
1909 };
1910 /**
1911  * @class Roo.lib.Dom
1912  * @licence LGPL
1913  * @static
1914  * 
1915  * Dom utils (from YIU afaik)
1916  *
1917  * 
1918  **/
1919 Roo.lib.Dom = {
1920     /**
1921      * Get the view width
1922      * @param {Boolean} full True will get the full document, otherwise it's the view width
1923      * @return {Number} The width
1924      */
1925      
1926     getViewWidth : function(full) {
1927         return full ? this.getDocumentWidth() : this.getViewportWidth();
1928     },
1929     /**
1930      * Get the view height
1931      * @param {Boolean} full True will get the full document, otherwise it's the view height
1932      * @return {Number} The height
1933      */
1934     getViewHeight : function(full) {
1935         return full ? this.getDocumentHeight() : this.getViewportHeight();
1936     },
1937     /**
1938      * Get the Full Document height 
1939      * @return {Number} The height
1940      */
1941     getDocumentHeight: function() {
1942         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1943         return Math.max(scrollHeight, this.getViewportHeight());
1944     },
1945     /**
1946      * Get the Full Document width
1947      * @return {Number} The width
1948      */
1949     getDocumentWidth: function() {
1950         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1951         return Math.max(scrollWidth, this.getViewportWidth());
1952     },
1953     /**
1954      * Get the Window Viewport height
1955      * @return {Number} The height
1956      */
1957     getViewportHeight: function() {
1958         var height = self.innerHeight;
1959         var mode = document.compatMode;
1960
1961         if ((mode || Roo.isIE) && !Roo.isOpera) {
1962             height = (mode == "CSS1Compat") ?
1963                      document.documentElement.clientHeight :
1964                      document.body.clientHeight;
1965         }
1966
1967         return height;
1968     },
1969     /**
1970      * Get the Window Viewport width
1971      * @return {Number} The width
1972      */
1973     getViewportWidth: function() {
1974         var width = self.innerWidth;
1975         var mode = document.compatMode;
1976
1977         if (mode || Roo.isIE) {
1978             width = (mode == "CSS1Compat") ?
1979                     document.documentElement.clientWidth :
1980                     document.body.clientWidth;
1981         }
1982         return width;
1983     },
1984
1985     isAncestor : function(p, c) {
1986         p = Roo.getDom(p);
1987         c = Roo.getDom(c);
1988         if (!p || !c) {
1989             return false;
1990         }
1991
1992         if (p.contains && !Roo.isSafari) {
1993             return p.contains(c);
1994         } else if (p.compareDocumentPosition) {
1995             return !!(p.compareDocumentPosition(c) & 16);
1996         } else {
1997             var parent = c.parentNode;
1998             while (parent) {
1999                 if (parent == p) {
2000                     return true;
2001                 }
2002                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2003                     return false;
2004                 }
2005                 parent = parent.parentNode;
2006             }
2007             return false;
2008         }
2009     },
2010
2011     getRegion : function(el) {
2012         return Roo.lib.Region.getRegion(el);
2013     },
2014
2015     getY : function(el) {
2016         return this.getXY(el)[1];
2017     },
2018
2019     getX : function(el) {
2020         return this.getXY(el)[0];
2021     },
2022
2023     getXY : function(el) {
2024         var p, pe, b, scroll, bd = document.body;
2025         el = Roo.getDom(el);
2026         var fly = Roo.lib.AnimBase.fly;
2027         if (el.getBoundingClientRect) {
2028             b = el.getBoundingClientRect();
2029             scroll = fly(document).getScroll();
2030             return [b.left + scroll.left, b.top + scroll.top];
2031         }
2032         var x = 0, y = 0;
2033
2034         p = el;
2035
2036         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2037
2038         while (p) {
2039
2040             x += p.offsetLeft;
2041             y += p.offsetTop;
2042
2043             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2044                 hasAbsolute = true;
2045             }
2046
2047             if (Roo.isGecko) {
2048                 pe = fly(p);
2049
2050                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2051                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2052
2053
2054                 x += bl;
2055                 y += bt;
2056
2057
2058                 if (p != el && pe.getStyle('overflow') != 'visible') {
2059                     x += bl;
2060                     y += bt;
2061                 }
2062             }
2063             p = p.offsetParent;
2064         }
2065
2066         if (Roo.isSafari && hasAbsolute) {
2067             x -= bd.offsetLeft;
2068             y -= bd.offsetTop;
2069         }
2070
2071         if (Roo.isGecko && !hasAbsolute) {
2072             var dbd = fly(bd);
2073             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2074             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2075         }
2076
2077         p = el.parentNode;
2078         while (p && p != bd) {
2079             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2080                 x -= p.scrollLeft;
2081                 y -= p.scrollTop;
2082             }
2083             p = p.parentNode;
2084         }
2085         return [x, y];
2086     },
2087  
2088   
2089
2090
2091     setXY : function(el, xy) {
2092         el = Roo.fly(el, '_setXY');
2093         el.position();
2094         var pts = el.translatePoints(xy);
2095         if (xy[0] !== false) {
2096             el.dom.style.left = pts.left + "px";
2097         }
2098         if (xy[1] !== false) {
2099             el.dom.style.top = pts.top + "px";
2100         }
2101     },
2102
2103     setX : function(el, x) {
2104         this.setXY(el, [x, false]);
2105     },
2106
2107     setY : function(el, y) {
2108         this.setXY(el, [false, y]);
2109     }
2110 };
2111 /*
2112  * Portions of this file are based on pieces of Yahoo User Interface Library
2113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2114  * YUI licensed under the BSD License:
2115  * http://developer.yahoo.net/yui/license.txt
2116  * <script type="text/javascript">
2117  *
2118  */
2119
2120 Roo.lib.Event = function() {
2121     var loadComplete = false;
2122     var listeners = [];
2123     var unloadListeners = [];
2124     var retryCount = 0;
2125     var onAvailStack = [];
2126     var counter = 0;
2127     var lastError = null;
2128
2129     return {
2130         POLL_RETRYS: 200,
2131         POLL_INTERVAL: 20,
2132         EL: 0,
2133         TYPE: 1,
2134         FN: 2,
2135         WFN: 3,
2136         OBJ: 3,
2137         ADJ_SCOPE: 4,
2138         _interval: null,
2139
2140         startInterval: function() {
2141             if (!this._interval) {
2142                 var self = this;
2143                 var callback = function() {
2144                     self._tryPreloadAttach();
2145                 };
2146                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2147
2148             }
2149         },
2150
2151         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2152             onAvailStack.push({ id:         p_id,
2153                 fn:         p_fn,
2154                 obj:        p_obj,
2155                 override:   p_override,
2156                 checkReady: false    });
2157
2158             retryCount = this.POLL_RETRYS;
2159             this.startInterval();
2160         },
2161
2162
2163         addListener: function(el, eventName, fn) {
2164             el = Roo.getDom(el);
2165             if (!el || !fn) {
2166                 return false;
2167             }
2168
2169             if ("unload" == eventName) {
2170                 unloadListeners[unloadListeners.length] =
2171                 [el, eventName, fn];
2172                 return true;
2173             }
2174
2175             var wrappedFn = function(e) {
2176                 return fn(Roo.lib.Event.getEvent(e));
2177             };
2178
2179             var li = [el, eventName, fn, wrappedFn];
2180
2181             var index = listeners.length;
2182             listeners[index] = li;
2183
2184             this.doAdd(el, eventName, wrappedFn, false);
2185             return true;
2186
2187         },
2188
2189
2190         removeListener: function(el, eventName, fn) {
2191             var i, len;
2192
2193             el = Roo.getDom(el);
2194
2195             if(!fn) {
2196                 return this.purgeElement(el, false, eventName);
2197             }
2198
2199
2200             if ("unload" == eventName) {
2201
2202                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2203                     var li = unloadListeners[i];
2204                     if (li &&
2205                         li[0] == el &&
2206                         li[1] == eventName &&
2207                         li[2] == fn) {
2208                         unloadListeners.splice(i, 1);
2209                         return true;
2210                     }
2211                 }
2212
2213                 return false;
2214             }
2215
2216             var cacheItem = null;
2217
2218
2219             var index = arguments[3];
2220
2221             if ("undefined" == typeof index) {
2222                 index = this._getCacheIndex(el, eventName, fn);
2223             }
2224
2225             if (index >= 0) {
2226                 cacheItem = listeners[index];
2227             }
2228
2229             if (!el || !cacheItem) {
2230                 return false;
2231             }
2232
2233             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2234
2235             delete listeners[index][this.WFN];
2236             delete listeners[index][this.FN];
2237             listeners.splice(index, 1);
2238
2239             return true;
2240
2241         },
2242
2243
2244         getTarget: function(ev, resolveTextNode) {
2245             ev = ev.browserEvent || ev;
2246             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2247             var t = ev.target || ev.srcElement;
2248             return this.resolveTextNode(t);
2249         },
2250
2251
2252         resolveTextNode: function(node) {
2253             if (Roo.isSafari && node && 3 == node.nodeType) {
2254                 return node.parentNode;
2255             } else {
2256                 return node;
2257             }
2258         },
2259
2260
2261         getPageX: function(ev) {
2262             ev = ev.browserEvent || ev;
2263             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2264             var x = ev.pageX;
2265             if (!x && 0 !== x) {
2266                 x = ev.clientX || 0;
2267
2268                 if (Roo.isIE) {
2269                     x += this.getScroll()[1];
2270                 }
2271             }
2272
2273             return x;
2274         },
2275
2276
2277         getPageY: function(ev) {
2278             ev = ev.browserEvent || ev;
2279             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2280             var y = ev.pageY;
2281             if (!y && 0 !== y) {
2282                 y = ev.clientY || 0;
2283
2284                 if (Roo.isIE) {
2285                     y += this.getScroll()[0];
2286                 }
2287             }
2288
2289
2290             return y;
2291         },
2292
2293
2294         getXY: function(ev) {
2295             ev = ev.browserEvent || ev;
2296             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2297             return [this.getPageX(ev), this.getPageY(ev)];
2298         },
2299
2300
2301         getRelatedTarget: function(ev) {
2302             ev = ev.browserEvent || ev;
2303             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2304             var t = ev.relatedTarget;
2305             if (!t) {
2306                 if (ev.type == "mouseout") {
2307                     t = ev.toElement;
2308                 } else if (ev.type == "mouseover") {
2309                     t = ev.fromElement;
2310                 }
2311             }
2312
2313             return this.resolveTextNode(t);
2314         },
2315
2316
2317         getTime: function(ev) {
2318             ev = ev.browserEvent || ev;
2319             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2320             if (!ev.time) {
2321                 var t = new Date().getTime();
2322                 try {
2323                     ev.time = t;
2324                 } catch(ex) {
2325                     this.lastError = ex;
2326                     return t;
2327                 }
2328             }
2329
2330             return ev.time;
2331         },
2332
2333
2334         stopEvent: function(ev) {
2335             this.stopPropagation(ev);
2336             this.preventDefault(ev);
2337         },
2338
2339
2340         stopPropagation: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if (ev.stopPropagation) {
2343                 ev.stopPropagation();
2344             } else {
2345                 ev.cancelBubble = true;
2346             }
2347         },
2348
2349
2350         preventDefault: function(ev) {
2351             ev = ev.browserEvent || ev;
2352             if(ev.preventDefault) {
2353                 ev.preventDefault();
2354             } else {
2355                 ev.returnValue = false;
2356             }
2357         },
2358
2359
2360         getEvent: function(e) {
2361             var ev = e || window.event;
2362             if (!ev) {
2363                 var c = this.getEvent.caller;
2364                 while (c) {
2365                     ev = c.arguments[0];
2366                     if (ev && Event == ev.constructor) {
2367                         break;
2368                     }
2369                     c = c.caller;
2370                 }
2371             }
2372             return ev;
2373         },
2374
2375
2376         getCharCode: function(ev) {
2377             ev = ev.browserEvent || ev;
2378             return ev.charCode || ev.keyCode || 0;
2379         },
2380
2381
2382         _getCacheIndex: function(el, eventName, fn) {
2383             for (var i = 0,len = listeners.length; i < len; ++i) {
2384                 var li = listeners[i];
2385                 if (li &&
2386                     li[this.FN] == fn &&
2387                     li[this.EL] == el &&
2388                     li[this.TYPE] == eventName) {
2389                     return i;
2390                 }
2391             }
2392
2393             return -1;
2394         },
2395
2396
2397         elCache: {},
2398
2399
2400         getEl: function(id) {
2401             return document.getElementById(id);
2402         },
2403
2404
2405         clearCache: function() {
2406         },
2407
2408
2409         _load: function(e) {
2410             loadComplete = true;
2411             var EU = Roo.lib.Event;
2412
2413
2414             if (Roo.isIE) {
2415                 EU.doRemove(window, "load", EU._load);
2416             }
2417         },
2418
2419
2420         _tryPreloadAttach: function() {
2421
2422             if (this.locked) {
2423                 return false;
2424             }
2425
2426             this.locked = true;
2427
2428
2429             var tryAgain = !loadComplete;
2430             if (!tryAgain) {
2431                 tryAgain = (retryCount > 0);
2432             }
2433
2434
2435             var notAvail = [];
2436             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2437                 var item = onAvailStack[i];
2438                 if (item) {
2439                     var el = this.getEl(item.id);
2440
2441                     if (el) {
2442                         if (!item.checkReady ||
2443                             loadComplete ||
2444                             el.nextSibling ||
2445                             (document && document.body)) {
2446
2447                             var scope = el;
2448                             if (item.override) {
2449                                 if (item.override === true) {
2450                                     scope = item.obj;
2451                                 } else {
2452                                     scope = item.override;
2453                                 }
2454                             }
2455                             item.fn.call(scope, item.obj);
2456                             onAvailStack[i] = null;
2457                         }
2458                     } else {
2459                         notAvail.push(item);
2460                     }
2461                 }
2462             }
2463
2464             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2465
2466             if (tryAgain) {
2467
2468                 this.startInterval();
2469             } else {
2470                 clearInterval(this._interval);
2471                 this._interval = null;
2472             }
2473
2474             this.locked = false;
2475
2476             return true;
2477
2478         },
2479
2480
2481         purgeElement: function(el, recurse, eventName) {
2482             var elListeners = this.getListeners(el, eventName);
2483             if (elListeners) {
2484                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2485                     var l = elListeners[i];
2486                     this.removeListener(el, l.type, l.fn);
2487                 }
2488             }
2489
2490             if (recurse && el && el.childNodes) {
2491                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2492                     this.purgeElement(el.childNodes[i], recurse, eventName);
2493                 }
2494             }
2495         },
2496
2497
2498         getListeners: function(el, eventName) {
2499             var results = [], searchLists;
2500             if (!eventName) {
2501                 searchLists = [listeners, unloadListeners];
2502             } else if (eventName == "unload") {
2503                 searchLists = [unloadListeners];
2504             } else {
2505                 searchLists = [listeners];
2506             }
2507
2508             for (var j = 0; j < searchLists.length; ++j) {
2509                 var searchList = searchLists[j];
2510                 if (searchList && searchList.length > 0) {
2511                     for (var i = 0,len = searchList.length; i < len; ++i) {
2512                         var l = searchList[i];
2513                         if (l && l[this.EL] === el &&
2514                             (!eventName || eventName === l[this.TYPE])) {
2515                             results.push({
2516                                 type:   l[this.TYPE],
2517                                 fn:     l[this.FN],
2518                                 obj:    l[this.OBJ],
2519                                 adjust: l[this.ADJ_SCOPE],
2520                                 index:  i
2521                             });
2522                         }
2523                     }
2524                 }
2525             }
2526
2527             return (results.length) ? results : null;
2528         },
2529
2530
2531         _unload: function(e) {
2532
2533             var EU = Roo.lib.Event, i, j, l, len, index;
2534
2535             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2536                 l = unloadListeners[i];
2537                 if (l) {
2538                     var scope = window;
2539                     if (l[EU.ADJ_SCOPE]) {
2540                         if (l[EU.ADJ_SCOPE] === true) {
2541                             scope = l[EU.OBJ];
2542                         } else {
2543                             scope = l[EU.ADJ_SCOPE];
2544                         }
2545                     }
2546                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2547                     unloadListeners[i] = null;
2548                     l = null;
2549                     scope = null;
2550                 }
2551             }
2552
2553             unloadListeners = null;
2554
2555             if (listeners && listeners.length > 0) {
2556                 j = listeners.length;
2557                 while (j) {
2558                     index = j - 1;
2559                     l = listeners[index];
2560                     if (l) {
2561                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2562                                 l[EU.FN], index);
2563                     }
2564                     j = j - 1;
2565                 }
2566                 l = null;
2567
2568                 EU.clearCache();
2569             }
2570
2571             EU.doRemove(window, "unload", EU._unload);
2572
2573         },
2574
2575
2576         getScroll: function() {
2577             var dd = document.documentElement, db = document.body;
2578             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2579                 return [dd.scrollTop, dd.scrollLeft];
2580             } else if (db) {
2581                 return [db.scrollTop, db.scrollLeft];
2582             } else {
2583                 return [0, 0];
2584             }
2585         },
2586
2587
2588         doAdd: function () {
2589             if (window.addEventListener) {
2590                 return function(el, eventName, fn, capture) {
2591                     el.addEventListener(eventName, fn, (capture));
2592                 };
2593             } else if (window.attachEvent) {
2594                 return function(el, eventName, fn, capture) {
2595                     el.attachEvent("on" + eventName, fn);
2596                 };
2597             } else {
2598                 return function() {
2599                 };
2600             }
2601         }(),
2602
2603
2604         doRemove: function() {
2605             if (window.removeEventListener) {
2606                 return function (el, eventName, fn, capture) {
2607                     el.removeEventListener(eventName, fn, (capture));
2608                 };
2609             } else if (window.detachEvent) {
2610                 return function (el, eventName, fn) {
2611                     el.detachEvent("on" + eventName, fn);
2612                 };
2613             } else {
2614                 return function() {
2615                 };
2616             }
2617         }()
2618     };
2619     
2620 }();
2621 (function() {     
2622    
2623     var E = Roo.lib.Event;
2624     E.on = E.addListener;
2625     E.un = E.removeListener;
2626
2627     if (document && document.body) {
2628         E._load();
2629     } else {
2630         E.doAdd(window, "load", E._load);
2631     }
2632     E.doAdd(window, "unload", E._unload);
2633     E._tryPreloadAttach();
2634 })();
2635
2636  
2637
2638 (function() {
2639     /**
2640      * @class Roo.lib.Ajax
2641      *
2642      * provide a simple Ajax request utility functions
2643      * 
2644      * Portions of this file are based on pieces of Yahoo User Interface Library
2645     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2646     * YUI licensed under the BSD License:
2647     * http://developer.yahoo.net/yui/license.txt
2648     * <script type="text/javascript">
2649     *
2650      *
2651      */
2652     Roo.lib.Ajax = {
2653         /**
2654          * @static 
2655          */
2656         request : function(method, uri, cb, data, options) {
2657             if(options){
2658                 var hs = options.headers;
2659                 if(hs){
2660                     for(var h in hs){
2661                         if(hs.hasOwnProperty(h)){
2662                             this.initHeader(h, hs[h], false);
2663                         }
2664                     }
2665                 }
2666                 if(options.xmlData){
2667                     this.initHeader('Content-Type', 'text/xml', false);
2668                     method = 'POST';
2669                     data = options.xmlData;
2670                 }
2671             }
2672
2673             return this.asyncRequest(method, uri, cb, data);
2674         },
2675         /**
2676          * serialize a form
2677          *
2678          * @static
2679          * @param {DomForm} form element
2680          * @return {String} urlencode form output.
2681          */
2682         serializeForm : function(form) {
2683             if(typeof form == 'string') {
2684                 form = (document.getElementById(form) || document.forms[form]);
2685             }
2686
2687             var el, name, val, disabled, data = '', hasSubmit = false;
2688             for (var i = 0; i < form.elements.length; i++) {
2689                 el = form.elements[i];
2690                 disabled = form.elements[i].disabled;
2691                 name = form.elements[i].name;
2692                 val = form.elements[i].value;
2693
2694                 if (!disabled && name){
2695                     switch (el.type)
2696                             {
2697                         case 'select-one':
2698                         case 'select-multiple':
2699                             for (var j = 0; j < el.options.length; j++) {
2700                                 if (el.options[j].selected) {
2701                                     if (Roo.isIE) {
2702                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2703                                     }
2704                                     else {
2705                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2706                                     }
2707                                 }
2708                             }
2709                             break;
2710                         case 'radio':
2711                         case 'checkbox':
2712                             if (el.checked) {
2713                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2714                             }
2715                             break;
2716                         case 'file':
2717
2718                         case undefined:
2719
2720                         case 'reset':
2721
2722                         case 'button':
2723
2724                             break;
2725                         case 'submit':
2726                             if(hasSubmit == false) {
2727                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2728                                 hasSubmit = true;
2729                             }
2730                             break;
2731                         default:
2732                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2733                             break;
2734                     }
2735                 }
2736             }
2737             data = data.substr(0, data.length - 1);
2738             return data;
2739         },
2740
2741         headers:{},
2742
2743         hasHeaders:false,
2744
2745         useDefaultHeader:true,
2746
2747         defaultPostHeader:'application/x-www-form-urlencoded',
2748
2749         useDefaultXhrHeader:true,
2750
2751         defaultXhrHeader:'XMLHttpRequest',
2752
2753         hasDefaultHeaders:true,
2754
2755         defaultHeaders:{},
2756
2757         poll:{},
2758
2759         timeout:{},
2760
2761         pollInterval:50,
2762
2763         transactionId:0,
2764
2765         setProgId:function(id)
2766         {
2767             this.activeX.unshift(id);
2768         },
2769
2770         setDefaultPostHeader:function(b)
2771         {
2772             this.useDefaultHeader = b;
2773         },
2774
2775         setDefaultXhrHeader:function(b)
2776         {
2777             this.useDefaultXhrHeader = b;
2778         },
2779
2780         setPollingInterval:function(i)
2781         {
2782             if (typeof i == 'number' && isFinite(i)) {
2783                 this.pollInterval = i;
2784             }
2785         },
2786
2787         createXhrObject:function(transactionId)
2788         {
2789             var obj,http;
2790             try
2791             {
2792
2793                 http = new XMLHttpRequest();
2794
2795                 obj = { conn:http, tId:transactionId };
2796             }
2797             catch(e)
2798             {
2799                 for (var i = 0; i < this.activeX.length; ++i) {
2800                     try
2801                     {
2802
2803                         http = new ActiveXObject(this.activeX[i]);
2804
2805                         obj = { conn:http, tId:transactionId };
2806                         break;
2807                     }
2808                     catch(e) {
2809                     }
2810                 }
2811             }
2812             finally
2813             {
2814                 return obj;
2815             }
2816         },
2817
2818         getConnectionObject:function()
2819         {
2820             var o;
2821             var tId = this.transactionId;
2822
2823             try
2824             {
2825                 o = this.createXhrObject(tId);
2826                 if (o) {
2827                     this.transactionId++;
2828                 }
2829             }
2830             catch(e) {
2831             }
2832             finally
2833             {
2834                 return o;
2835             }
2836         },
2837
2838         asyncRequest:function(method, uri, callback, postData)
2839         {
2840             var o = this.getConnectionObject();
2841
2842             if (!o) {
2843                 return null;
2844             }
2845             else {
2846                 o.conn.open(method, uri, true);
2847
2848                 if (this.useDefaultXhrHeader) {
2849                     if (!this.defaultHeaders['X-Requested-With']) {
2850                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2851                     }
2852                 }
2853
2854                 if(postData && this.useDefaultHeader){
2855                     this.initHeader('Content-Type', this.defaultPostHeader);
2856                 }
2857
2858                  if (this.hasDefaultHeaders || this.hasHeaders) {
2859                     this.setHeader(o);
2860                 }
2861
2862                 this.handleReadyState(o, callback);
2863                 o.conn.send(postData || null);
2864
2865                 return o;
2866             }
2867         },
2868
2869         handleReadyState:function(o, callback)
2870         {
2871             var oConn = this;
2872
2873             if (callback && callback.timeout) {
2874                 
2875                 this.timeout[o.tId] = window.setTimeout(function() {
2876                     oConn.abort(o, callback, true);
2877                 }, callback.timeout);
2878             }
2879
2880             this.poll[o.tId] = window.setInterval(
2881                     function() {
2882                         if (o.conn && o.conn.readyState == 4) {
2883                             window.clearInterval(oConn.poll[o.tId]);
2884                             delete oConn.poll[o.tId];
2885
2886                             if(callback && callback.timeout) {
2887                                 window.clearTimeout(oConn.timeout[o.tId]);
2888                                 delete oConn.timeout[o.tId];
2889                             }
2890
2891                             oConn.handleTransactionResponse(o, callback);
2892                         }
2893                     }
2894                     , this.pollInterval);
2895         },
2896
2897         handleTransactionResponse:function(o, callback, isAbort)
2898         {
2899
2900             if (!callback) {
2901                 this.releaseObject(o);
2902                 return;
2903             }
2904
2905             var httpStatus, responseObject;
2906
2907             try
2908             {
2909                 if (o.conn.status !== undefined && o.conn.status != 0) {
2910                     httpStatus = o.conn.status;
2911                 }
2912                 else {
2913                     httpStatus = 13030;
2914                 }
2915             }
2916             catch(e) {
2917
2918
2919                 httpStatus = 13030;
2920             }
2921
2922             if (httpStatus >= 200 && httpStatus < 300) {
2923                 responseObject = this.createResponseObject(o, callback.argument);
2924                 if (callback.success) {
2925                     if (!callback.scope) {
2926                         callback.success(responseObject);
2927                     }
2928                     else {
2929
2930
2931                         callback.success.apply(callback.scope, [responseObject]);
2932                     }
2933                 }
2934             }
2935             else {
2936                 switch (httpStatus) {
2937
2938                     case 12002:
2939                     case 12029:
2940                     case 12030:
2941                     case 12031:
2942                     case 12152:
2943                     case 13030:
2944                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2945                         if (callback.failure) {
2946                             if (!callback.scope) {
2947                                 callback.failure(responseObject);
2948                             }
2949                             else {
2950                                 callback.failure.apply(callback.scope, [responseObject]);
2951                             }
2952                         }
2953                         break;
2954                     default:
2955                         responseObject = this.createResponseObject(o, callback.argument);
2956                         if (callback.failure) {
2957                             if (!callback.scope) {
2958                                 callback.failure(responseObject);
2959                             }
2960                             else {
2961                                 callback.failure.apply(callback.scope, [responseObject]);
2962                             }
2963                         }
2964                 }
2965             }
2966
2967             this.releaseObject(o);
2968             responseObject = null;
2969         },
2970
2971         createResponseObject:function(o, callbackArg)
2972         {
2973             var obj = {};
2974             var headerObj = {};
2975
2976             try
2977             {
2978                 var headerStr = o.conn.getAllResponseHeaders();
2979                 var header = headerStr.split('\n');
2980                 for (var i = 0; i < header.length; i++) {
2981                     var delimitPos = header[i].indexOf(':');
2982                     if (delimitPos != -1) {
2983                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2984                     }
2985                 }
2986             }
2987             catch(e) {
2988             }
2989
2990             obj.tId = o.tId;
2991             obj.status = o.conn.status;
2992             obj.statusText = o.conn.statusText;
2993             obj.getResponseHeader = headerObj;
2994             obj.getAllResponseHeaders = headerStr;
2995             obj.responseText = o.conn.responseText;
2996             obj.responseXML = o.conn.responseXML;
2997
2998             if (typeof callbackArg !== undefined) {
2999                 obj.argument = callbackArg;
3000             }
3001
3002             return obj;
3003         },
3004
3005         createExceptionObject:function(tId, callbackArg, isAbort)
3006         {
3007             var COMM_CODE = 0;
3008             var COMM_ERROR = 'communication failure';
3009             var ABORT_CODE = -1;
3010             var ABORT_ERROR = 'transaction aborted';
3011
3012             var obj = {};
3013
3014             obj.tId = tId;
3015             if (isAbort) {
3016                 obj.status = ABORT_CODE;
3017                 obj.statusText = ABORT_ERROR;
3018             }
3019             else {
3020                 obj.status = COMM_CODE;
3021                 obj.statusText = COMM_ERROR;
3022             }
3023
3024             if (callbackArg) {
3025                 obj.argument = callbackArg;
3026             }
3027
3028             return obj;
3029         },
3030
3031         initHeader:function(label, value, isDefault)
3032         {
3033             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3034
3035             if (headerObj[label] === undefined) {
3036                 headerObj[label] = value;
3037             }
3038             else {
3039
3040
3041                 headerObj[label] = value + "," + headerObj[label];
3042             }
3043
3044             if (isDefault) {
3045                 this.hasDefaultHeaders = true;
3046             }
3047             else {
3048                 this.hasHeaders = true;
3049             }
3050         },
3051
3052
3053         setHeader:function(o)
3054         {
3055             if (this.hasDefaultHeaders) {
3056                 for (var prop in this.defaultHeaders) {
3057                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3058                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3059                     }
3060                 }
3061             }
3062
3063             if (this.hasHeaders) {
3064                 for (var prop in this.headers) {
3065                     if (this.headers.hasOwnProperty(prop)) {
3066                         o.conn.setRequestHeader(prop, this.headers[prop]);
3067                     }
3068                 }
3069                 this.headers = {};
3070                 this.hasHeaders = false;
3071             }
3072         },
3073
3074         resetDefaultHeaders:function() {
3075             delete this.defaultHeaders;
3076             this.defaultHeaders = {};
3077             this.hasDefaultHeaders = false;
3078         },
3079
3080         abort:function(o, callback, isTimeout)
3081         {
3082             if(this.isCallInProgress(o)) {
3083                 o.conn.abort();
3084                 window.clearInterval(this.poll[o.tId]);
3085                 delete this.poll[o.tId];
3086                 if (isTimeout) {
3087                     delete this.timeout[o.tId];
3088                 }
3089
3090                 this.handleTransactionResponse(o, callback, true);
3091
3092                 return true;
3093             }
3094             else {
3095                 return false;
3096             }
3097         },
3098
3099
3100         isCallInProgress:function(o)
3101         {
3102             if (o && o.conn) {
3103                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3104             }
3105             else {
3106
3107                 return false;
3108             }
3109         },
3110
3111
3112         releaseObject:function(o)
3113         {
3114
3115             o.conn = null;
3116
3117             o = null;
3118         },
3119
3120         activeX:[
3121         'MSXML2.XMLHTTP.3.0',
3122         'MSXML2.XMLHTTP',
3123         'Microsoft.XMLHTTP'
3124         ]
3125
3126
3127     };
3128 })();/*
3129  * Portions of this file are based on pieces of Yahoo User Interface Library
3130  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3131  * YUI licensed under the BSD License:
3132  * http://developer.yahoo.net/yui/license.txt
3133  * <script type="text/javascript">
3134  *
3135  */
3136
3137 Roo.lib.Region = function(t, r, b, l) {
3138     this.top = t;
3139     this[1] = t;
3140     this.right = r;
3141     this.bottom = b;
3142     this.left = l;
3143     this[0] = l;
3144 };
3145
3146
3147 Roo.lib.Region.prototype = {
3148     contains : function(region) {
3149         return ( region.left >= this.left &&
3150                  region.right <= this.right &&
3151                  region.top >= this.top &&
3152                  region.bottom <= this.bottom    );
3153
3154     },
3155
3156     getArea : function() {
3157         return ( (this.bottom - this.top) * (this.right - this.left) );
3158     },
3159
3160     intersect : function(region) {
3161         var t = Math.max(this.top, region.top);
3162         var r = Math.min(this.right, region.right);
3163         var b = Math.min(this.bottom, region.bottom);
3164         var l = Math.max(this.left, region.left);
3165
3166         if (b >= t && r >= l) {
3167             return new Roo.lib.Region(t, r, b, l);
3168         } else {
3169             return null;
3170         }
3171     },
3172     union : function(region) {
3173         var t = Math.min(this.top, region.top);
3174         var r = Math.max(this.right, region.right);
3175         var b = Math.max(this.bottom, region.bottom);
3176         var l = Math.min(this.left, region.left);
3177
3178         return new Roo.lib.Region(t, r, b, l);
3179     },
3180
3181     adjust : function(t, l, b, r) {
3182         this.top += t;
3183         this.left += l;
3184         this.right += r;
3185         this.bottom += b;
3186         return this;
3187     }
3188 };
3189
3190 Roo.lib.Region.getRegion = function(el) {
3191     var p = Roo.lib.Dom.getXY(el);
3192
3193     var t = p[1];
3194     var r = p[0] + el.offsetWidth;
3195     var b = p[1] + el.offsetHeight;
3196     var l = p[0];
3197
3198     return new Roo.lib.Region(t, r, b, l);
3199 };
3200 /*
3201  * Portions of this file are based on pieces of Yahoo User Interface Library
3202  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3203  * YUI licensed under the BSD License:
3204  * http://developer.yahoo.net/yui/license.txt
3205  * <script type="text/javascript">
3206  *
3207  */
3208 //@@dep Roo.lib.Region
3209
3210
3211 Roo.lib.Point = function(x, y) {
3212     if (x instanceof Array) {
3213         y = x[1];
3214         x = x[0];
3215     }
3216     this.x = this.right = this.left = this[0] = x;
3217     this.y = this.top = this.bottom = this[1] = y;
3218 };
3219
3220 Roo.lib.Point.prototype = new Roo.lib.Region();
3221 /*
3222  * Portions of this file are based on pieces of Yahoo User Interface Library
3223  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3224  * YUI licensed under the BSD License:
3225  * http://developer.yahoo.net/yui/license.txt
3226  * <script type="text/javascript">
3227  *
3228  */
3229  
3230 (function() {   
3231
3232     Roo.lib.Anim = {
3233         scroll : function(el, args, duration, easing, cb, scope) {
3234             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3235         },
3236
3237         motion : function(el, args, duration, easing, cb, scope) {
3238             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3239         },
3240
3241         color : function(el, args, duration, easing, cb, scope) {
3242             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3243         },
3244
3245         run : function(el, args, duration, easing, cb, scope, type) {
3246             type = type || Roo.lib.AnimBase;
3247             if (typeof easing == "string") {
3248                 easing = Roo.lib.Easing[easing];
3249             }
3250             var anim = new type(el, args, duration, easing);
3251             anim.animateX(function() {
3252                 Roo.callback(cb, scope);
3253             });
3254             return anim;
3255         }
3256     };
3257 })();/*
3258  * Portions of this file are based on pieces of Yahoo User Interface Library
3259  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3260  * YUI licensed under the BSD License:
3261  * http://developer.yahoo.net/yui/license.txt
3262  * <script type="text/javascript">
3263  *
3264  */
3265
3266 (function() {    
3267     var libFlyweight;
3268     
3269     function fly(el) {
3270         if (!libFlyweight) {
3271             libFlyweight = new Roo.Element.Flyweight();
3272         }
3273         libFlyweight.dom = el;
3274         return libFlyweight;
3275     }
3276
3277     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3278     
3279    
3280     
3281     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3282         if (el) {
3283             this.init(el, attributes, duration, method);
3284         }
3285     };
3286
3287     Roo.lib.AnimBase.fly = fly;
3288     
3289     
3290     
3291     Roo.lib.AnimBase.prototype = {
3292
3293         toString: function() {
3294             var el = this.getEl();
3295             var id = el.id || el.tagName;
3296             return ("Anim " + id);
3297         },
3298
3299         patterns: {
3300             noNegatives:        /width|height|opacity|padding/i,
3301             offsetAttribute:  /^((width|height)|(top|left))$/,
3302             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3303             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3304         },
3305
3306
3307         doMethod: function(attr, start, end) {
3308             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3309         },
3310
3311
3312         setAttribute: function(attr, val, unit) {
3313             if (this.patterns.noNegatives.test(attr)) {
3314                 val = (val > 0) ? val : 0;
3315             }
3316
3317             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3318         },
3319
3320
3321         getAttribute: function(attr) {
3322             var el = this.getEl();
3323             var val = fly(el).getStyle(attr);
3324
3325             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3326                 return parseFloat(val);
3327             }
3328
3329             var a = this.patterns.offsetAttribute.exec(attr) || [];
3330             var pos = !!( a[3] );
3331             var box = !!( a[2] );
3332
3333
3334             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3335                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3336             } else {
3337                 val = 0;
3338             }
3339
3340             return val;
3341         },
3342
3343
3344         getDefaultUnit: function(attr) {
3345             if (this.patterns.defaultUnit.test(attr)) {
3346                 return 'px';
3347             }
3348
3349             return '';
3350         },
3351
3352         animateX : function(callback, scope) {
3353             var f = function() {
3354                 this.onComplete.removeListener(f);
3355                 if (typeof callback == "function") {
3356                     callback.call(scope || this, this);
3357                 }
3358             };
3359             this.onComplete.addListener(f, this);
3360             this.animate();
3361         },
3362
3363
3364         setRuntimeAttribute: function(attr) {
3365             var start;
3366             var end;
3367             var attributes = this.attributes;
3368
3369             this.runtimeAttributes[attr] = {};
3370
3371             var isset = function(prop) {
3372                 return (typeof prop !== 'undefined');
3373             };
3374
3375             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3376                 return false;
3377             }
3378
3379             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3380
3381
3382             if (isset(attributes[attr]['to'])) {
3383                 end = attributes[attr]['to'];
3384             } else if (isset(attributes[attr]['by'])) {
3385                 if (start.constructor == Array) {
3386                     end = [];
3387                     for (var i = 0, len = start.length; i < len; ++i) {
3388                         end[i] = start[i] + attributes[attr]['by'][i];
3389                     }
3390                 } else {
3391                     end = start + attributes[attr]['by'];
3392                 }
3393             }
3394
3395             this.runtimeAttributes[attr].start = start;
3396             this.runtimeAttributes[attr].end = end;
3397
3398
3399             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3400         },
3401
3402
3403         init: function(el, attributes, duration, method) {
3404
3405             var isAnimated = false;
3406
3407
3408             var startTime = null;
3409
3410
3411             var actualFrames = 0;
3412
3413
3414             el = Roo.getDom(el);
3415
3416
3417             this.attributes = attributes || {};
3418
3419
3420             this.duration = duration || 1;
3421
3422
3423             this.method = method || Roo.lib.Easing.easeNone;
3424
3425
3426             this.useSeconds = true;
3427
3428
3429             this.currentFrame = 0;
3430
3431
3432             this.totalFrames = Roo.lib.AnimMgr.fps;
3433
3434
3435             this.getEl = function() {
3436                 return el;
3437             };
3438
3439
3440             this.isAnimated = function() {
3441                 return isAnimated;
3442             };
3443
3444
3445             this.getStartTime = function() {
3446                 return startTime;
3447             };
3448
3449             this.runtimeAttributes = {};
3450
3451
3452             this.animate = function() {
3453                 if (this.isAnimated()) {
3454                     return false;
3455                 }
3456
3457                 this.currentFrame = 0;
3458
3459                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3460
3461                 Roo.lib.AnimMgr.registerElement(this);
3462             };
3463
3464
3465             this.stop = function(finish) {
3466                 if (finish) {
3467                     this.currentFrame = this.totalFrames;
3468                     this._onTween.fire();
3469                 }
3470                 Roo.lib.AnimMgr.stop(this);
3471             };
3472
3473             var onStart = function() {
3474                 this.onStart.fire();
3475
3476                 this.runtimeAttributes = {};
3477                 for (var attr in this.attributes) {
3478                     this.setRuntimeAttribute(attr);
3479                 }
3480
3481                 isAnimated = true;
3482                 actualFrames = 0;
3483                 startTime = new Date();
3484             };
3485
3486
3487             var onTween = function() {
3488                 var data = {
3489                     duration: new Date() - this.getStartTime(),
3490                     currentFrame: this.currentFrame
3491                 };
3492
3493                 data.toString = function() {
3494                     return (
3495                             'duration: ' + data.duration +
3496                             ', currentFrame: ' + data.currentFrame
3497                             );
3498                 };
3499
3500                 this.onTween.fire(data);
3501
3502                 var runtimeAttributes = this.runtimeAttributes;
3503
3504                 for (var attr in runtimeAttributes) {
3505                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3506                 }
3507
3508                 actualFrames += 1;
3509             };
3510
3511             var onComplete = function() {
3512                 var actual_duration = (new Date() - startTime) / 1000 ;
3513
3514                 var data = {
3515                     duration: actual_duration,
3516                     frames: actualFrames,
3517                     fps: actualFrames / actual_duration
3518                 };
3519
3520                 data.toString = function() {
3521                     return (
3522                             'duration: ' + data.duration +
3523                             ', frames: ' + data.frames +
3524                             ', fps: ' + data.fps
3525                             );
3526                 };
3527
3528                 isAnimated = false;
3529                 actualFrames = 0;
3530                 this.onComplete.fire(data);
3531             };
3532
3533
3534             this._onStart = new Roo.util.Event(this);
3535             this.onStart = new Roo.util.Event(this);
3536             this.onTween = new Roo.util.Event(this);
3537             this._onTween = new Roo.util.Event(this);
3538             this.onComplete = new Roo.util.Event(this);
3539             this._onComplete = new Roo.util.Event(this);
3540             this._onStart.addListener(onStart);
3541             this._onTween.addListener(onTween);
3542             this._onComplete.addListener(onComplete);
3543         }
3544     };
3545 })();
3546 /*
3547  * Portions of this file are based on pieces of Yahoo User Interface Library
3548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3549  * YUI licensed under the BSD License:
3550  * http://developer.yahoo.net/yui/license.txt
3551  * <script type="text/javascript">
3552  *
3553  */
3554
3555 Roo.lib.AnimMgr = new function() {
3556
3557     var thread = null;
3558
3559
3560     var queue = [];
3561
3562
3563     var tweenCount = 0;
3564
3565
3566     this.fps = 1000;
3567
3568
3569     this.delay = 1;
3570
3571
3572     this.registerElement = function(tween) {
3573         queue[queue.length] = tween;
3574         tweenCount += 1;
3575         tween._onStart.fire();
3576         this.start();
3577     };
3578
3579
3580     this.unRegister = function(tween, index) {
3581         tween._onComplete.fire();
3582         index = index || getIndex(tween);
3583         if (index != -1) {
3584             queue.splice(index, 1);
3585         }
3586
3587         tweenCount -= 1;
3588         if (tweenCount <= 0) {
3589             this.stop();
3590         }
3591     };
3592
3593
3594     this.start = function() {
3595         if (thread === null) {
3596             thread = setInterval(this.run, this.delay);
3597         }
3598     };
3599
3600
3601     this.stop = function(tween) {
3602         if (!tween) {
3603             clearInterval(thread);
3604
3605             for (var i = 0, len = queue.length; i < len; ++i) {
3606                 if (queue[0].isAnimated()) {
3607                     this.unRegister(queue[0], 0);
3608                 }
3609             }
3610
3611             queue = [];
3612             thread = null;
3613             tweenCount = 0;
3614         }
3615         else {
3616             this.unRegister(tween);
3617         }
3618     };
3619
3620
3621     this.run = function() {
3622         for (var i = 0, len = queue.length; i < len; ++i) {
3623             var tween = queue[i];
3624             if (!tween || !tween.isAnimated()) {
3625                 continue;
3626             }
3627
3628             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3629             {
3630                 tween.currentFrame += 1;
3631
3632                 if (tween.useSeconds) {
3633                     correctFrame(tween);
3634                 }
3635                 tween._onTween.fire();
3636             }
3637             else {
3638                 Roo.lib.AnimMgr.stop(tween, i);
3639             }
3640         }
3641     };
3642
3643     var getIndex = function(anim) {
3644         for (var i = 0, len = queue.length; i < len; ++i) {
3645             if (queue[i] == anim) {
3646                 return i;
3647             }
3648         }
3649         return -1;
3650     };
3651
3652
3653     var correctFrame = function(tween) {
3654         var frames = tween.totalFrames;
3655         var frame = tween.currentFrame;
3656         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3657         var elapsed = (new Date() - tween.getStartTime());
3658         var tweak = 0;
3659
3660         if (elapsed < tween.duration * 1000) {
3661             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3662         } else {
3663             tweak = frames - (frame + 1);
3664         }
3665         if (tweak > 0 && isFinite(tweak)) {
3666             if (tween.currentFrame + tweak >= frames) {
3667                 tweak = frames - (frame + 1);
3668             }
3669
3670             tween.currentFrame += tweak;
3671         }
3672     };
3673 };
3674
3675     /*
3676  * Portions of this file are based on pieces of Yahoo User Interface Library
3677  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3678  * YUI licensed under the BSD License:
3679  * http://developer.yahoo.net/yui/license.txt
3680  * <script type="text/javascript">
3681  *
3682  */
3683 Roo.lib.Bezier = new function() {
3684
3685         this.getPosition = function(points, t) {
3686             var n = points.length;
3687             var tmp = [];
3688
3689             for (var i = 0; i < n; ++i) {
3690                 tmp[i] = [points[i][0], points[i][1]];
3691             }
3692
3693             for (var j = 1; j < n; ++j) {
3694                 for (i = 0; i < n - j; ++i) {
3695                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3696                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3697                 }
3698             }
3699
3700             return [ tmp[0][0], tmp[0][1] ];
3701
3702         };
3703     }; 
3704
3705 /**
3706  * @class Roo.lib.Color
3707  * @constructor
3708  * An abstract Color implementation. Concrete Color implementations should use
3709  * an instance of this function as their prototype, and implement the getRGB and
3710  * getHSL functions. getRGB should return an object representing the RGB
3711  * components of this Color, with the red, green, and blue components in the
3712  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3713  * return an object representing the HSL components of this Color, with the hue
3714  * component in the range [0,360), the saturation and lightness components in
3715  * the range [0,100], and the alpha component in the range [0,1].
3716  *
3717  *
3718  * Color.js
3719  *
3720  * Functions for Color handling and processing.
3721  *
3722  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3723  *
3724  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3725  * rights to this program, with the intention of it becoming part of the public
3726  * domain. Because this program is released into the public domain, it comes with
3727  * no warranty either expressed or implied, to the extent permitted by law.
3728  * 
3729  * For more free and public domain JavaScript code by the same author, visit:
3730  * http://www.safalra.com/web-design/javascript/
3731  * 
3732  */
3733 Roo.lib.Color = function() { }
3734
3735
3736 Roo.apply(Roo.lib.Color.prototype, {
3737   
3738   rgb : null,
3739   hsv : null,
3740   hsl : null,
3741   
3742   /**
3743    * getIntegerRGB
3744    * @return {Object} an object representing the RGBA components of this Color. The red,
3745    * green, and blue components are converted to integers in the range [0,255].
3746    * The alpha is a value in the range [0,1].
3747    */
3748   getIntegerRGB : function(){
3749
3750     // get the RGB components of this Color
3751     var rgb = this.getRGB();
3752
3753     // return the integer components
3754     return {
3755       'r' : Math.round(rgb.r),
3756       'g' : Math.round(rgb.g),
3757       'b' : Math.round(rgb.b),
3758       'a' : rgb.a
3759     };
3760
3761   },
3762
3763   /**
3764    * getPercentageRGB
3765    * @return {Object} an object representing the RGBA components of this Color. The red,
3766    * green, and blue components are converted to numbers in the range [0,100].
3767    * The alpha is a value in the range [0,1].
3768    */
3769   getPercentageRGB : function(){
3770
3771     // get the RGB components of this Color
3772     var rgb = this.getRGB();
3773
3774     // return the percentage components
3775     return {
3776       'r' : 100 * rgb.r / 255,
3777       'g' : 100 * rgb.g / 255,
3778       'b' : 100 * rgb.b / 255,
3779       'a' : rgb.a
3780     };
3781
3782   },
3783
3784   /**
3785    * getCSSHexadecimalRGB
3786    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3787    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3788    * are two-digit hexadecimal numbers.
3789    */
3790   getCSSHexadecimalRGB : function()
3791   {
3792
3793     // get the integer RGB components
3794     var rgb = this.getIntegerRGB();
3795
3796     // determine the hexadecimal equivalents
3797     var r16 = rgb.r.toString(16);
3798     var g16 = rgb.g.toString(16);
3799     var b16 = rgb.b.toString(16);
3800
3801     // return the CSS RGB Color value
3802     return '#'
3803         + (r16.length == 2 ? r16 : '0' + r16)
3804         + (g16.length == 2 ? g16 : '0' + g16)
3805         + (b16.length == 2 ? b16 : '0' + b16);
3806
3807   },
3808
3809   /**
3810    * getCSSIntegerRGB
3811    * @return {String} a string representing this Color as a CSS integer RGB Color
3812    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3813    * are integers in the range [0,255].
3814    */
3815   getCSSIntegerRGB : function(){
3816
3817     // get the integer RGB components
3818     var rgb = this.getIntegerRGB();
3819
3820     // return the CSS RGB Color value
3821     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3822
3823   },
3824
3825   /**
3826    * getCSSIntegerRGBA
3827    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3828    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3829    * b are integers in the range [0,255] and a is in the range [0,1].
3830    */
3831   getCSSIntegerRGBA : function(){
3832
3833     // get the integer RGB components
3834     var rgb = this.getIntegerRGB();
3835
3836     // return the CSS integer RGBA Color value
3837     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3838
3839   },
3840
3841   /**
3842    * getCSSPercentageRGB
3843    * @return {String} a string representing this Color as a CSS percentage RGB Color
3844    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3845    * b are in the range [0,100].
3846    */
3847   getCSSPercentageRGB : function(){
3848
3849     // get the percentage RGB components
3850     var rgb = this.getPercentageRGB();
3851
3852     // return the CSS RGB Color value
3853     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3854
3855   },
3856
3857   /**
3858    * getCSSPercentageRGBA
3859    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3860    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3861    * and b are in the range [0,100] and a is in the range [0,1].
3862    */
3863   getCSSPercentageRGBA : function(){
3864
3865     // get the percentage RGB components
3866     var rgb = this.getPercentageRGB();
3867
3868     // return the CSS percentage RGBA Color value
3869     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3870
3871   },
3872
3873   /**
3874    * getCSSHSL
3875    * @return {String} a string representing this Color as a CSS HSL Color value - that
3876    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3877    * s and l are in the range [0,100].
3878    */
3879   getCSSHSL : function(){
3880
3881     // get the HSL components
3882     var hsl = this.getHSL();
3883
3884     // return the CSS HSL Color value
3885     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3886
3887   },
3888
3889   /**
3890    * getCSSHSLA
3891    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3892    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3893    * s and l are in the range [0,100], and a is in the range [0,1].
3894    */
3895   getCSSHSLA : function(){
3896
3897     // get the HSL components
3898     var hsl = this.getHSL();
3899
3900     // return the CSS HSL Color value
3901     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3902
3903   },
3904
3905   /**
3906    * Sets the Color of the specified node to this Color. This functions sets
3907    * the CSS 'color' property for the node. The parameter is:
3908    * 
3909    * @param {DomElement} node - the node whose Color should be set
3910    */
3911   setNodeColor : function(node){
3912
3913     // set the Color of the node
3914     node.style.color = this.getCSSHexadecimalRGB();
3915
3916   },
3917
3918   /**
3919    * Sets the background Color of the specified node to this Color. This
3920    * functions sets the CSS 'background-color' property for the node. The
3921    * parameter is:
3922    *
3923    * @param {DomElement} node - the node whose background Color should be set
3924    */
3925   setNodeBackgroundColor : function(node){
3926
3927     // set the background Color of the node
3928     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3929
3930   },
3931   // convert between formats..
3932   toRGB: function()
3933   {
3934     var r = this.getIntegerRGB();
3935     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3936     
3937   },
3938   toHSL : function()
3939   {
3940      var hsl = this.getHSL();
3941   // return the CSS HSL Color value
3942     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3943     
3944   },
3945   
3946   toHSV : function()
3947   {
3948     var rgb = this.toRGB();
3949     var hsv = rgb.getHSV();
3950    // return the CSS HSL Color value
3951     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3952     
3953   },
3954   
3955   // modify  v = 0 ... 1 (eg. 0.5)
3956   saturate : function(v)
3957   {
3958       var rgb = this.toRGB();
3959       var hsv = rgb.getHSV();
3960       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3961       
3962   
3963   },
3964   
3965    
3966   /**
3967    * getRGB
3968    * @return {Object} the RGB and alpha components of this Color as an object with r,
3969    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3970    * the range [0,1].
3971    */
3972   getRGB: function(){
3973    
3974     // return the RGB components
3975     return {
3976       'r' : this.rgb.r,
3977       'g' : this.rgb.g,
3978       'b' : this.rgb.b,
3979       'a' : this.alpha
3980     };
3981
3982   },
3983
3984   /**
3985    * getHSV
3986    * @return {Object} the HSV and alpha components of this Color as an object with h,
3987    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3988    * [0,100], and a is in the range [0,1].
3989    */
3990   getHSV : function()
3991   {
3992     
3993     // calculate the HSV components if necessary
3994     if (this.hsv == null) {
3995       this.calculateHSV();
3996     }
3997
3998     // return the HSV components
3999     return {
4000       'h' : this.hsv.h,
4001       's' : this.hsv.s,
4002       'v' : this.hsv.v,
4003       'a' : this.alpha
4004     };
4005
4006   },
4007
4008   /**
4009    * getHSL
4010    * @return {Object} the HSL and alpha components of this Color as an object with h,
4011    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4012    * [0,100], and a is in the range [0,1].
4013    */
4014   getHSL : function(){
4015     
4016      
4017     // calculate the HSV components if necessary
4018     if (this.hsl == null) { this.calculateHSL(); }
4019
4020     // return the HSL components
4021     return {
4022       'h' : this.hsl.h,
4023       's' : this.hsl.s,
4024       'l' : this.hsl.l,
4025       'a' : this.alpha
4026     };
4027
4028   }
4029   
4030
4031 });
4032
4033
4034 /**
4035  * @class Roo.lib.RGBColor
4036  * @extends Roo.lib.Color
4037  * Creates a Color specified in the RGB Color space, with an optional alpha
4038  * component. The parameters are:
4039  * @constructor
4040  * 
4041
4042  * @param {Number} r - the red component, clipped to the range [0,255]
4043  * @param {Number} g - the green component, clipped to the range [0,255]
4044  * @param {Number} b - the blue component, clipped to the range [0,255]
4045  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4046  *     optional and defaults to 1
4047  */
4048 Roo.lib.RGBColor = function (r, g, b, a){
4049
4050   // store the alpha component after clipping it if necessary
4051   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4052
4053   // store the RGB components after clipping them if necessary
4054   this.rgb =
4055       {
4056         'r' : Math.max(0, Math.min(255, r)),
4057         'g' : Math.max(0, Math.min(255, g)),
4058         'b' : Math.max(0, Math.min(255, b))
4059       };
4060
4061   // initialise the HSV and HSL components to null
4062   
4063
4064   /* 
4065    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4066    * range [0,360). The parameters are:
4067    *
4068    * maximum - the maximum of the RGB component values
4069    * range   - the range of the RGB component values
4070    */
4071    
4072
4073 }
4074 // this does an 'exteds'
4075 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4076
4077   
4078     getHue  : function(maximum, range)
4079     {
4080       var rgb = this.rgb;
4081        
4082       // check whether the range is zero
4083       if (range == 0){
4084   
4085         // set the hue to zero (any hue is acceptable as the Color is grey)
4086         var hue = 0;
4087   
4088       }else{
4089   
4090         // determine which of the components has the highest value and set the hue
4091         switch (maximum){
4092   
4093           // red has the highest value
4094           case rgb.r:
4095             var hue = (rgb.g - rgb.b) / range * 60;
4096             if (hue < 0) { hue += 360; }
4097             break;
4098   
4099           // green has the highest value
4100           case rgb.g:
4101             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4102             break;
4103   
4104           // blue has the highest value
4105           case rgb.b:
4106             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4107             break;
4108   
4109         }
4110   
4111       }
4112   
4113       // return the hue
4114       return hue;
4115   
4116     },
4117
4118   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4119    * be returned be the getHSV function.
4120    */
4121    calculateHSV : function(){
4122     var rgb = this.rgb;
4123     // get the maximum and range of the RGB component values
4124     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4125     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4126
4127     // store the HSV components
4128     this.hsv =
4129         {
4130           'h' : this.getHue(maximum, range),
4131           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4132           'v' : maximum / 2.55
4133         };
4134
4135   },
4136
4137   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4138    * be returned be the getHSL function.
4139    */
4140    calculateHSL : function(){
4141     var rgb = this.rgb;
4142     // get the maximum and range of the RGB component values
4143     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4144     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4145
4146     // determine the lightness in the range [0,1]
4147     var l = maximum / 255 - range / 510;
4148
4149     // store the HSL components
4150     this.hsl =
4151         {
4152           'h' : this.getHue(maximum, range),
4153           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4154           'l' : 100 * l
4155         };
4156
4157   }
4158
4159 });
4160
4161 /**
4162  * @class Roo.lib.HSVColor
4163  * @extends Roo.lib.Color
4164  * Creates a Color specified in the HSV Color space, with an optional alpha
4165  * component. The parameters are:
4166  * @constructor
4167  *
4168  * @param {Number} h - the hue component, wrapped to the range [0,360)
4169  * @param {Number} s - the saturation component, clipped to the range [0,100]
4170  * @param {Number} v - the value component, clipped to the range [0,100]
4171  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4172  *     optional and defaults to 1
4173  */
4174 Roo.lib.HSVColor = function (h, s, v, a){
4175
4176   // store the alpha component after clipping it if necessary
4177   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4178
4179   // store the HSV components after clipping or wrapping them if necessary
4180   this.hsv =
4181       {
4182         'h' : (h % 360 + 360) % 360,
4183         's' : Math.max(0, Math.min(100, s)),
4184         'v' : Math.max(0, Math.min(100, v))
4185       };
4186
4187   // initialise the RGB and HSL components to null
4188   this.rgb = null;
4189   this.hsl = null;
4190 }
4191
4192 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4193   /* Calculates and stores the RGB components of this HSVColor so that they can
4194    * be returned be the getRGB function.
4195    */
4196   calculateRGB: function ()
4197   {
4198     var hsv = this.hsv;
4199     // check whether the saturation is zero
4200     if (hsv.s == 0){
4201
4202       // set the Color to the appropriate shade of grey
4203       var r = hsv.v;
4204       var g = hsv.v;
4205       var b = hsv.v;
4206
4207     }else{
4208
4209       // set some temporary values
4210       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4211       var p  = hsv.v * (1 - hsv.s / 100);
4212       var q  = hsv.v * (1 - hsv.s / 100 * f);
4213       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4214
4215       // set the RGB Color components to their temporary values
4216       switch (Math.floor(hsv.h / 60)){
4217         case 0: var r = hsv.v; var g = t; var b = p; break;
4218         case 1: var r = q; var g = hsv.v; var b = p; break;
4219         case 2: var r = p; var g = hsv.v; var b = t; break;
4220         case 3: var r = p; var g = q; var b = hsv.v; break;
4221         case 4: var r = t; var g = p; var b = hsv.v; break;
4222         case 5: var r = hsv.v; var g = p; var b = q; break;
4223       }
4224
4225     }
4226
4227     // store the RGB components
4228     this.rgb =
4229         {
4230           'r' : r * 2.55,
4231           'g' : g * 2.55,
4232           'b' : b * 2.55
4233         };
4234
4235   },
4236
4237   /* Calculates and stores the HSL components of this HSVColor so that they can
4238    * be returned be the getHSL function.
4239    */
4240   calculateHSL : function (){
4241
4242     var hsv = this.hsv;
4243     // determine the lightness in the range [0,100]
4244     var l = (2 - hsv.s / 100) * hsv.v / 2;
4245
4246     // store the HSL components
4247     this.hsl =
4248         {
4249           'h' : hsv.h,
4250           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4251           'l' : l
4252         };
4253
4254     // correct a division-by-zero error
4255     if (isNaN(hsl.s)) { hsl.s = 0; }
4256
4257   } 
4258  
4259
4260 });
4261  
4262
4263 /**
4264  * @class Roo.lib.HSLColor
4265  * @extends Roo.lib.Color
4266  *
4267  * @constructor
4268  * Creates a Color specified in the HSL Color space, with an optional alpha
4269  * component. The parameters are:
4270  *
4271  * @param {Number} h - the hue component, wrapped to the range [0,360)
4272  * @param {Number} s - the saturation component, clipped to the range [0,100]
4273  * @param {Number} l - the lightness component, clipped to the range [0,100]
4274  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4275  *     optional and defaults to 1
4276  */
4277
4278 Roo.lib.HSLColor = function(h, s, l, a){
4279
4280   // store the alpha component after clipping it if necessary
4281   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4282
4283   // store the HSL components after clipping or wrapping them if necessary
4284   this.hsl =
4285       {
4286         'h' : (h % 360 + 360) % 360,
4287         's' : Math.max(0, Math.min(100, s)),
4288         'l' : Math.max(0, Math.min(100, l))
4289       };
4290
4291   // initialise the RGB and HSV components to null
4292 }
4293
4294 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4295
4296   /* Calculates and stores the RGB components of this HSLColor so that they can
4297    * be returned be the getRGB function.
4298    */
4299   calculateRGB: function (){
4300
4301     // check whether the saturation is zero
4302     if (this.hsl.s == 0){
4303
4304       // store the RGB components representing the appropriate shade of grey
4305       this.rgb =
4306           {
4307             'r' : this.hsl.l * 2.55,
4308             'g' : this.hsl.l * 2.55,
4309             'b' : this.hsl.l * 2.55
4310           };
4311
4312     }else{
4313
4314       // set some temporary values
4315       var p = this.hsl.l < 50
4316             ? this.hsl.l * (1 + hsl.s / 100)
4317             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4318       var q = 2 * hsl.l - p;
4319
4320       // initialise the RGB components
4321       this.rgb =
4322           {
4323             'r' : (h + 120) / 60 % 6,
4324             'g' : h / 60,
4325             'b' : (h + 240) / 60 % 6
4326           };
4327
4328       // loop over the RGB components
4329       for (var key in this.rgb){
4330
4331         // ensure that the property is not inherited from the root object
4332         if (this.rgb.hasOwnProperty(key)){
4333
4334           // set the component to its value in the range [0,100]
4335           if (this.rgb[key] < 1){
4336             this.rgb[key] = q + (p - q) * this.rgb[key];
4337           }else if (this.rgb[key] < 3){
4338             this.rgb[key] = p;
4339           }else if (this.rgb[key] < 4){
4340             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4341           }else{
4342             this.rgb[key] = q;
4343           }
4344
4345           // set the component to its value in the range [0,255]
4346           this.rgb[key] *= 2.55;
4347
4348         }
4349
4350       }
4351
4352     }
4353
4354   },
4355
4356   /* Calculates and stores the HSV components of this HSLColor so that they can
4357    * be returned be the getHSL function.
4358    */
4359    calculateHSV : function(){
4360
4361     // set a temporary value
4362     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4363
4364     // store the HSV components
4365     this.hsv =
4366         {
4367           'h' : this.hsl.h,
4368           's' : 200 * t / (this.hsl.l + t),
4369           'v' : t + this.hsl.l
4370         };
4371
4372     // correct a division-by-zero error
4373     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4374
4375   }
4376  
4377
4378 });
4379 /*
4380  * Portions of this file are based on pieces of Yahoo User Interface Library
4381  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4382  * YUI licensed under the BSD License:
4383  * http://developer.yahoo.net/yui/license.txt
4384  * <script type="text/javascript">
4385  *
4386  */
4387 (function() {
4388
4389     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4390         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4391     };
4392
4393     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4394
4395     var fly = Roo.lib.AnimBase.fly;
4396     var Y = Roo.lib;
4397     var superclass = Y.ColorAnim.superclass;
4398     var proto = Y.ColorAnim.prototype;
4399
4400     proto.toString = function() {
4401         var el = this.getEl();
4402         var id = el.id || el.tagName;
4403         return ("ColorAnim " + id);
4404     };
4405
4406     proto.patterns.color = /color$/i;
4407     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4408     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4409     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4410     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4411
4412
4413     proto.parseColor = function(s) {
4414         if (s.length == 3) {
4415             return s;
4416         }
4417
4418         var c = this.patterns.hex.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4421         }
4422
4423         c = this.patterns.rgb.exec(s);
4424         if (c && c.length == 4) {
4425             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4426         }
4427
4428         c = this.patterns.hex3.exec(s);
4429         if (c && c.length == 4) {
4430             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4431         }
4432
4433         return null;
4434     };
4435     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4436     proto.getAttribute = function(attr) {
4437         var el = this.getEl();
4438         if (this.patterns.color.test(attr)) {
4439             var val = fly(el).getStyle(attr);
4440
4441             if (this.patterns.transparent.test(val)) {
4442                 var parent = el.parentNode;
4443                 val = fly(parent).getStyle(attr);
4444
4445                 while (parent && this.patterns.transparent.test(val)) {
4446                     parent = parent.parentNode;
4447                     val = fly(parent).getStyle(attr);
4448                     if (parent.tagName.toUpperCase() == 'HTML') {
4449                         val = '#fff';
4450                     }
4451                 }
4452             }
4453         } else {
4454             val = superclass.getAttribute.call(this, attr);
4455         }
4456
4457         return val;
4458     };
4459     proto.getAttribute = function(attr) {
4460         var el = this.getEl();
4461         if (this.patterns.color.test(attr)) {
4462             var val = fly(el).getStyle(attr);
4463
4464             if (this.patterns.transparent.test(val)) {
4465                 var parent = el.parentNode;
4466                 val = fly(parent).getStyle(attr);
4467
4468                 while (parent && this.patterns.transparent.test(val)) {
4469                     parent = parent.parentNode;
4470                     val = fly(parent).getStyle(attr);
4471                     if (parent.tagName.toUpperCase() == 'HTML') {
4472                         val = '#fff';
4473                     }
4474                 }
4475             }
4476         } else {
4477             val = superclass.getAttribute.call(this, attr);
4478         }
4479
4480         return val;
4481     };
4482
4483     proto.doMethod = function(attr, start, end) {
4484         var val;
4485
4486         if (this.patterns.color.test(attr)) {
4487             val = [];
4488             for (var i = 0, len = start.length; i < len; ++i) {
4489                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4490             }
4491
4492             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4493         }
4494         else {
4495             val = superclass.doMethod.call(this, attr, start, end);
4496         }
4497
4498         return val;
4499     };
4500
4501     proto.setRuntimeAttribute = function(attr) {
4502         superclass.setRuntimeAttribute.call(this, attr);
4503
4504         if (this.patterns.color.test(attr)) {
4505             var attributes = this.attributes;
4506             var start = this.parseColor(this.runtimeAttributes[attr].start);
4507             var end = this.parseColor(this.runtimeAttributes[attr].end);
4508
4509             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4510                 end = this.parseColor(attributes[attr].by);
4511
4512                 for (var i = 0, len = start.length; i < len; ++i) {
4513                     end[i] = start[i] + end[i];
4514                 }
4515             }
4516
4517             this.runtimeAttributes[attr].start = start;
4518             this.runtimeAttributes[attr].end = end;
4519         }
4520     };
4521 })();
4522
4523 /*
4524  * Portions of this file are based on pieces of Yahoo User Interface Library
4525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4526  * YUI licensed under the BSD License:
4527  * http://developer.yahoo.net/yui/license.txt
4528  * <script type="text/javascript">
4529  *
4530  */
4531 Roo.lib.Easing = {
4532
4533
4534     easeNone: function (t, b, c, d) {
4535         return c * t / d + b;
4536     },
4537
4538
4539     easeIn: function (t, b, c, d) {
4540         return c * (t /= d) * t + b;
4541     },
4542
4543
4544     easeOut: function (t, b, c, d) {
4545         return -c * (t /= d) * (t - 2) + b;
4546     },
4547
4548
4549     easeBoth: function (t, b, c, d) {
4550         if ((t /= d / 2) < 1) {
4551             return c / 2 * t * t + b;
4552         }
4553
4554         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4555     },
4556
4557
4558     easeInStrong: function (t, b, c, d) {
4559         return c * (t /= d) * t * t * t + b;
4560     },
4561
4562
4563     easeOutStrong: function (t, b, c, d) {
4564         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4565     },
4566
4567
4568     easeBothStrong: function (t, b, c, d) {
4569         if ((t /= d / 2) < 1) {
4570             return c / 2 * t * t * t * t + b;
4571         }
4572
4573         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4574     },
4575
4576
4577
4578     elasticIn: function (t, b, c, d, a, p) {
4579         if (t == 0) {
4580             return b;
4581         }
4582         if ((t /= d) == 1) {
4583             return b + c;
4584         }
4585         if (!p) {
4586             p = d * .3;
4587         }
4588
4589         if (!a || a < Math.abs(c)) {
4590             a = c;
4591             var s = p / 4;
4592         }
4593         else {
4594             var s = p / (2 * Math.PI) * Math.asin(c / a);
4595         }
4596
4597         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4598     },
4599
4600
4601     elasticOut: function (t, b, c, d, a, p) {
4602         if (t == 0) {
4603             return b;
4604         }
4605         if ((t /= d) == 1) {
4606             return b + c;
4607         }
4608         if (!p) {
4609             p = d * .3;
4610         }
4611
4612         if (!a || a < Math.abs(c)) {
4613             a = c;
4614             var s = p / 4;
4615         }
4616         else {
4617             var s = p / (2 * Math.PI) * Math.asin(c / a);
4618         }
4619
4620         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4621     },
4622
4623
4624     elasticBoth: function (t, b, c, d, a, p) {
4625         if (t == 0) {
4626             return b;
4627         }
4628
4629         if ((t /= d / 2) == 2) {
4630             return b + c;
4631         }
4632
4633         if (!p) {
4634             p = d * (.3 * 1.5);
4635         }
4636
4637         if (!a || a < Math.abs(c)) {
4638             a = c;
4639             var s = p / 4;
4640         }
4641         else {
4642             var s = p / (2 * Math.PI) * Math.asin(c / a);
4643         }
4644
4645         if (t < 1) {
4646             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4647                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4648         }
4649         return a * Math.pow(2, -10 * (t -= 1)) *
4650                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4651     },
4652
4653
4654
4655     backIn: function (t, b, c, d, s) {
4656         if (typeof s == 'undefined') {
4657             s = 1.70158;
4658         }
4659         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4660     },
4661
4662
4663     backOut: function (t, b, c, d, s) {
4664         if (typeof s == 'undefined') {
4665             s = 1.70158;
4666         }
4667         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4668     },
4669
4670
4671     backBoth: function (t, b, c, d, s) {
4672         if (typeof s == 'undefined') {
4673             s = 1.70158;
4674         }
4675
4676         if ((t /= d / 2 ) < 1) {
4677             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4678         }
4679         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4680     },
4681
4682
4683     bounceIn: function (t, b, c, d) {
4684         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4685     },
4686
4687
4688     bounceOut: function (t, b, c, d) {
4689         if ((t /= d) < (1 / 2.75)) {
4690             return c * (7.5625 * t * t) + b;
4691         } else if (t < (2 / 2.75)) {
4692             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4693         } else if (t < (2.5 / 2.75)) {
4694             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4695         }
4696         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4697     },
4698
4699
4700     bounceBoth: function (t, b, c, d) {
4701         if (t < d / 2) {
4702             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4703         }
4704         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4705     }
4706 };/*
4707  * Portions of this file are based on pieces of Yahoo User Interface Library
4708  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4709  * YUI licensed under the BSD License:
4710  * http://developer.yahoo.net/yui/license.txt
4711  * <script type="text/javascript">
4712  *
4713  */
4714     (function() {
4715         Roo.lib.Motion = function(el, attributes, duration, method) {
4716             if (el) {
4717                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4718             }
4719         };
4720
4721         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4722
4723
4724         var Y = Roo.lib;
4725         var superclass = Y.Motion.superclass;
4726         var proto = Y.Motion.prototype;
4727
4728         proto.toString = function() {
4729             var el = this.getEl();
4730             var id = el.id || el.tagName;
4731             return ("Motion " + id);
4732         };
4733
4734         proto.patterns.points = /^points$/i;
4735
4736         proto.setAttribute = function(attr, val, unit) {
4737             if (this.patterns.points.test(attr)) {
4738                 unit = unit || 'px';
4739                 superclass.setAttribute.call(this, 'left', val[0], unit);
4740                 superclass.setAttribute.call(this, 'top', val[1], unit);
4741             } else {
4742                 superclass.setAttribute.call(this, attr, val, unit);
4743             }
4744         };
4745
4746         proto.getAttribute = function(attr) {
4747             if (this.patterns.points.test(attr)) {
4748                 var val = [
4749                         superclass.getAttribute.call(this, 'left'),
4750                         superclass.getAttribute.call(this, 'top')
4751                         ];
4752             } else {
4753                 val = superclass.getAttribute.call(this, attr);
4754             }
4755
4756             return val;
4757         };
4758
4759         proto.doMethod = function(attr, start, end) {
4760             var val = null;
4761
4762             if (this.patterns.points.test(attr)) {
4763                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4764                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4765             } else {
4766                 val = superclass.doMethod.call(this, attr, start, end);
4767             }
4768             return val;
4769         };
4770
4771         proto.setRuntimeAttribute = function(attr) {
4772             if (this.patterns.points.test(attr)) {
4773                 var el = this.getEl();
4774                 var attributes = this.attributes;
4775                 var start;
4776                 var control = attributes['points']['control'] || [];
4777                 var end;
4778                 var i, len;
4779
4780                 if (control.length > 0 && !(control[0] instanceof Array)) {
4781                     control = [control];
4782                 } else {
4783                     var tmp = [];
4784                     for (i = 0,len = control.length; i < len; ++i) {
4785                         tmp[i] = control[i];
4786                     }
4787                     control = tmp;
4788                 }
4789
4790                 Roo.fly(el).position();
4791
4792                 if (isset(attributes['points']['from'])) {
4793                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4794                 }
4795                 else {
4796                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4797                 }
4798
4799                 start = this.getAttribute('points');
4800
4801
4802                 if (isset(attributes['points']['to'])) {
4803                     end = translateValues.call(this, attributes['points']['to'], start);
4804
4805                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4806                     for (i = 0,len = control.length; i < len; ++i) {
4807                         control[i] = translateValues.call(this, control[i], start);
4808                     }
4809
4810
4811                 } else if (isset(attributes['points']['by'])) {
4812                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4813
4814                     for (i = 0,len = control.length; i < len; ++i) {
4815                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4816                     }
4817                 }
4818
4819                 this.runtimeAttributes[attr] = [start];
4820
4821                 if (control.length > 0) {
4822                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4823                 }
4824
4825                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4826             }
4827             else {
4828                 superclass.setRuntimeAttribute.call(this, attr);
4829             }
4830         };
4831
4832         var translateValues = function(val, start) {
4833             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4834             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4835
4836             return val;
4837         };
4838
4839         var isset = function(prop) {
4840             return (typeof prop !== 'undefined');
4841         };
4842     })();
4843 /*
4844  * Portions of this file are based on pieces of Yahoo User Interface Library
4845  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4846  * YUI licensed under the BSD License:
4847  * http://developer.yahoo.net/yui/license.txt
4848  * <script type="text/javascript">
4849  *
4850  */
4851     (function() {
4852         Roo.lib.Scroll = function(el, attributes, duration, method) {
4853             if (el) {
4854                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4855             }
4856         };
4857
4858         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4859
4860
4861         var Y = Roo.lib;
4862         var superclass = Y.Scroll.superclass;
4863         var proto = Y.Scroll.prototype;
4864
4865         proto.toString = function() {
4866             var el = this.getEl();
4867             var id = el.id || el.tagName;
4868             return ("Scroll " + id);
4869         };
4870
4871         proto.doMethod = function(attr, start, end) {
4872             var val = null;
4873
4874             if (attr == 'scroll') {
4875                 val = [
4876                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4877                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4878                         ];
4879
4880             } else {
4881                 val = superclass.doMethod.call(this, attr, start, end);
4882             }
4883             return val;
4884         };
4885
4886         proto.getAttribute = function(attr) {
4887             var val = null;
4888             var el = this.getEl();
4889
4890             if (attr == 'scroll') {
4891                 val = [ el.scrollLeft, el.scrollTop ];
4892             } else {
4893                 val = superclass.getAttribute.call(this, attr);
4894             }
4895
4896             return val;
4897         };
4898
4899         proto.setAttribute = function(attr, val, unit) {
4900             var el = this.getEl();
4901
4902             if (attr == 'scroll') {
4903                 el.scrollLeft = val[0];
4904                 el.scrollTop = val[1];
4905             } else {
4906                 superclass.setAttribute.call(this, attr, val, unit);
4907             }
4908         };
4909     })();
4910 /*
4911  * Based on:
4912  * Ext JS Library 1.1.1
4913  * Copyright(c) 2006-2007, Ext JS, LLC.
4914  *
4915  * Originally Released Under LGPL - original licence link has changed is not relivant.
4916  *
4917  * Fork - LGPL
4918  * <script type="text/javascript">
4919  */
4920
4921
4922 // nasty IE9 hack - what a pile of crap that is..
4923
4924  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4925     Range.prototype.createContextualFragment = function (html) {
4926         var doc = window.document;
4927         var container = doc.createElement("div");
4928         container.innerHTML = html;
4929         var frag = doc.createDocumentFragment(), n;
4930         while ((n = container.firstChild)) {
4931             frag.appendChild(n);
4932         }
4933         return frag;
4934     };
4935 }
4936
4937 /**
4938  * @class Roo.DomHelper
4939  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4940  * 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>.
4941  * @static
4942  */
4943 Roo.DomHelper = function(){
4944     var tempTableEl = null;
4945     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4946     var tableRe = /^table|tbody|tr|td$/i;
4947     var xmlns = {};
4948     // build as innerHTML where available
4949     /** @ignore */
4950     var createHtml = function(o){
4951         if(typeof o == 'string'){
4952             return o;
4953         }
4954         var b = "";
4955         if(!o.tag){
4956             o.tag = "div";
4957         }
4958         b += "<" + o.tag;
4959         for(var attr in o){
4960             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4961             if(attr == "style"){
4962                 var s = o["style"];
4963                 if(typeof s == "function"){
4964                     s = s.call();
4965                 }
4966                 if(typeof s == "string"){
4967                     b += ' style="' + s + '"';
4968                 }else if(typeof s == "object"){
4969                     b += ' style="';
4970                     for(var key in s){
4971                         if(typeof s[key] != "function"){
4972                             b += key + ":" + s[key] + ";";
4973                         }
4974                     }
4975                     b += '"';
4976                 }
4977             }else{
4978                 if(attr == "cls"){
4979                     b += ' class="' + o["cls"] + '"';
4980                 }else if(attr == "htmlFor"){
4981                     b += ' for="' + o["htmlFor"] + '"';
4982                 }else{
4983                     b += " " + attr + '="' + o[attr] + '"';
4984                 }
4985             }
4986         }
4987         if(emptyTags.test(o.tag)){
4988             b += "/>";
4989         }else{
4990             b += ">";
4991             var cn = o.children || o.cn;
4992             if(cn){
4993                 //http://bugs.kde.org/show_bug.cgi?id=71506
4994                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4995                     for(var i = 0, len = cn.length; i < len; i++) {
4996                         b += createHtml(cn[i], b);
4997                     }
4998                 }else{
4999                     b += createHtml(cn, b);
5000                 }
5001             }
5002             if(o.html){
5003                 b += o.html;
5004             }
5005             b += "</" + o.tag + ">";
5006         }
5007         return b;
5008     };
5009
5010     // build as dom
5011     /** @ignore */
5012     var createDom = function(o, parentNode){
5013          
5014         // defininition craeted..
5015         var ns = false;
5016         if (o.ns && o.ns != 'html') {
5017                
5018             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5019                 xmlns[o.ns] = o.xmlns;
5020                 ns = o.xmlns;
5021             }
5022             if (typeof(xmlns[o.ns]) == 'undefined') {
5023                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5024             }
5025             ns = xmlns[o.ns];
5026         }
5027         
5028         
5029         if (typeof(o) == 'string') {
5030             return parentNode.appendChild(document.createTextNode(o));
5031         }
5032         o.tag = o.tag || div;
5033         if (o.ns && Roo.isIE) {
5034             ns = false;
5035             o.tag = o.ns + ':' + o.tag;
5036             
5037         }
5038         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5039         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5040         for(var attr in o){
5041             
5042             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5043                     attr == "style" || typeof o[attr] == "function") { continue; }
5044                     
5045             if(attr=="cls" && Roo.isIE){
5046                 el.className = o["cls"];
5047             }else{
5048                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5049                 else { 
5050                     el[attr] = o[attr];
5051                 }
5052             }
5053         }
5054         Roo.DomHelper.applyStyles(el, o.style);
5055         var cn = o.children || o.cn;
5056         if(cn){
5057             //http://bugs.kde.org/show_bug.cgi?id=71506
5058              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5059                 for(var i = 0, len = cn.length; i < len; i++) {
5060                     createDom(cn[i], el);
5061                 }
5062             }else{
5063                 createDom(cn, el);
5064             }
5065         }
5066         if(o.html){
5067             el.innerHTML = o.html;
5068         }
5069         if(parentNode){
5070            parentNode.appendChild(el);
5071         }
5072         return el;
5073     };
5074
5075     var ieTable = function(depth, s, h, e){
5076         tempTableEl.innerHTML = [s, h, e].join('');
5077         var i = -1, el = tempTableEl;
5078         while(++i < depth && el.firstChild){
5079             el = el.firstChild;
5080         }
5081         return el;
5082     };
5083
5084     // kill repeat to save bytes
5085     var ts = '<table>',
5086         te = '</table>',
5087         tbs = ts+'<tbody>',
5088         tbe = '</tbody>'+te,
5089         trs = tbs + '<tr>',
5090         tre = '</tr>'+tbe;
5091
5092     /**
5093      * @ignore
5094      * Nasty code for IE's broken table implementation
5095      */
5096     var insertIntoTable = function(tag, where, el, html){
5097         if(!tempTableEl){
5098             tempTableEl = document.createElement('div');
5099         }
5100         var node;
5101         var before = null;
5102         if(tag == 'td'){
5103             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5104                 return;
5105             }
5106             if(where == 'beforebegin'){
5107                 before = el;
5108                 el = el.parentNode;
5109             } else{
5110                 before = el.nextSibling;
5111                 el = el.parentNode;
5112             }
5113             node = ieTable(4, trs, html, tre);
5114         }
5115         else if(tag == 'tr'){
5116             if(where == 'beforebegin'){
5117                 before = el;
5118                 el = el.parentNode;
5119                 node = ieTable(3, tbs, html, tbe);
5120             } else if(where == 'afterend'){
5121                 before = el.nextSibling;
5122                 el = el.parentNode;
5123                 node = ieTable(3, tbs, html, tbe);
5124             } else{ // INTO a TR
5125                 if(where == 'afterbegin'){
5126                     before = el.firstChild;
5127                 }
5128                 node = ieTable(4, trs, html, tre);
5129             }
5130         } else if(tag == 'tbody'){
5131             if(where == 'beforebegin'){
5132                 before = el;
5133                 el = el.parentNode;
5134                 node = ieTable(2, ts, html, te);
5135             } else if(where == 'afterend'){
5136                 before = el.nextSibling;
5137                 el = el.parentNode;
5138                 node = ieTable(2, ts, html, te);
5139             } else{
5140                 if(where == 'afterbegin'){
5141                     before = el.firstChild;
5142                 }
5143                 node = ieTable(3, tbs, html, tbe);
5144             }
5145         } else{ // TABLE
5146             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5147                 return;
5148             }
5149             if(where == 'afterbegin'){
5150                 before = el.firstChild;
5151             }
5152             node = ieTable(2, ts, html, te);
5153         }
5154         el.insertBefore(node, before);
5155         return node;
5156     };
5157     
5158     // this is a bit like the react update code...
5159     // 
5160     
5161     var updateNode = function(from, to)
5162     {
5163         // should we handle non-standard elements?
5164         Roo.log(["UpdateNode" , from, to]);
5165         if (from.nodeType != to.nodeType) {
5166             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5167             from.parentNode.replaceChild(to, from);
5168         }
5169         
5170         if (from.nodeType == 3) {
5171             // assume it's text?!
5172             if (from.data == to.data) {
5173                 return;
5174             }
5175             from.data = to.data;
5176             return;
5177         }
5178         if (!from.parentNode) {
5179             // not sure why this is happening?
5180             return;
5181         }
5182         // assume 'to' doesnt have '1/3 nodetypes!
5183         // not sure why, by from, parent node might not exist?
5184         if (from.nodeType !=1 || from.tagName != to.tagName) {
5185             Roo.log(["ReplaceChild" , from, to ]);
5186             
5187             from.parentNode.replaceChild(to, from);
5188             return;
5189         }
5190         // compare attributes
5191         var ar = Array.from(from.attributes);
5192         for(var i = 0; i< ar.length;i++) {
5193             if (to.hasAttribute(ar[i].name)) {
5194                 continue;
5195             }
5196             if (ar[i].name == 'id') { // always keep ids?
5197                continue;
5198             }
5199             //if (ar[i].name == 'style') {
5200             //   throw "style removed?";
5201             //}
5202             Roo.log("removeAttribute" + ar[i].name);
5203             from.removeAttribute(ar[i].name);
5204         }
5205         ar = to.attributes;
5206         for(var i = 0; i< ar.length;i++) {
5207             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5208                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5209                 continue;
5210             }
5211             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5212             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5213         }
5214         // children
5215         var far = Array.from(from.childNodes);
5216         var tar = Array.from(to.childNodes);
5217         // if the lengths are different.. then it's probably a editable content change, rather than
5218         // a change of the block definition..
5219         
5220         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5221          /*if (from.innerHTML == to.innerHTML) {
5222             return;
5223         }
5224         if (far.length != tar.length) {
5225             from.innerHTML = to.innerHTML;
5226             return;
5227         }
5228         */
5229         
5230         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5231             if (i >= far.length) {
5232                 from.appendChild(tar[i]);
5233                 Roo.log(["add", tar[i]]);
5234                 
5235             } else if ( i  >= tar.length) {
5236                 from.removeChild(far[i]);
5237                 Roo.log(["remove", far[i]]);
5238             } else {
5239                 
5240                 updateNode(far[i], tar[i]);
5241             }    
5242         }
5243         
5244         
5245         
5246         
5247     };
5248     
5249     
5250
5251     return {
5252         /** True to force the use of DOM instead of html fragments @type Boolean */
5253         useDom : false,
5254     
5255         /**
5256          * Returns the markup for the passed Element(s) config
5257          * @param {Object} o The Dom object spec (and children)
5258          * @return {String}
5259          */
5260         markup : function(o){
5261             return createHtml(o);
5262         },
5263     
5264         /**
5265          * Applies a style specification to an element
5266          * @param {String/HTMLElement} el The element to apply styles to
5267          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5268          * a function which returns such a specification.
5269          */
5270         applyStyles : function(el, styles){
5271             if(styles){
5272                el = Roo.fly(el);
5273                if(typeof styles == "string"){
5274                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5275                    var matches;
5276                    while ((matches = re.exec(styles)) != null){
5277                        el.setStyle(matches[1], matches[2]);
5278                    }
5279                }else if (typeof styles == "object"){
5280                    for (var style in styles){
5281                       el.setStyle(style, styles[style]);
5282                    }
5283                }else if (typeof styles == "function"){
5284                     Roo.DomHelper.applyStyles(el, styles.call());
5285                }
5286             }
5287         },
5288     
5289         /**
5290          * Inserts an HTML fragment into the Dom
5291          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5292          * @param {HTMLElement} el The context element
5293          * @param {String} html The HTML fragmenet
5294          * @return {HTMLElement} The new node
5295          */
5296         insertHtml : function(where, el, html){
5297             where = where.toLowerCase();
5298             if(el.insertAdjacentHTML){
5299                 if(tableRe.test(el.tagName)){
5300                     var rs;
5301                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5302                         return rs;
5303                     }
5304                 }
5305                 switch(where){
5306                     case "beforebegin":
5307                         el.insertAdjacentHTML('BeforeBegin', html);
5308                         return el.previousSibling;
5309                     case "afterbegin":
5310                         el.insertAdjacentHTML('AfterBegin', html);
5311                         return el.firstChild;
5312                     case "beforeend":
5313                         el.insertAdjacentHTML('BeforeEnd', html);
5314                         return el.lastChild;
5315                     case "afterend":
5316                         el.insertAdjacentHTML('AfterEnd', html);
5317                         return el.nextSibling;
5318                 }
5319                 throw 'Illegal insertion point -> "' + where + '"';
5320             }
5321             var range = el.ownerDocument.createRange();
5322             var frag;
5323             switch(where){
5324                  case "beforebegin":
5325                     range.setStartBefore(el);
5326                     frag = range.createContextualFragment(html);
5327                     el.parentNode.insertBefore(frag, el);
5328                     return el.previousSibling;
5329                  case "afterbegin":
5330                     if(el.firstChild){
5331                         range.setStartBefore(el.firstChild);
5332                         frag = range.createContextualFragment(html);
5333                         el.insertBefore(frag, el.firstChild);
5334                         return el.firstChild;
5335                     }else{
5336                         el.innerHTML = html;
5337                         return el.firstChild;
5338                     }
5339                 case "beforeend":
5340                     if(el.lastChild){
5341                         range.setStartAfter(el.lastChild);
5342                         frag = range.createContextualFragment(html);
5343                         el.appendChild(frag);
5344                         return el.lastChild;
5345                     }else{
5346                         el.innerHTML = html;
5347                         return el.lastChild;
5348                     }
5349                 case "afterend":
5350                     range.setStartAfter(el);
5351                     frag = range.createContextualFragment(html);
5352                     el.parentNode.insertBefore(frag, el.nextSibling);
5353                     return el.nextSibling;
5354                 }
5355                 throw 'Illegal insertion point -> "' + where + '"';
5356         },
5357     
5358         /**
5359          * Creates new Dom element(s) and inserts them before el
5360          * @param {String/HTMLElement/Element} el The context element
5361          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5362          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5363          * @return {HTMLElement/Roo.Element} The new node
5364          */
5365         insertBefore : function(el, o, returnElement){
5366             return this.doInsert(el, o, returnElement, "beforeBegin");
5367         },
5368     
5369         /**
5370          * Creates new Dom element(s) and inserts them after el
5371          * @param {String/HTMLElement/Element} el The context element
5372          * @param {Object} o The Dom object spec (and children)
5373          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5374          * @return {HTMLElement/Roo.Element} The new node
5375          */
5376         insertAfter : function(el, o, returnElement){
5377             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5378         },
5379     
5380         /**
5381          * Creates new Dom element(s) and inserts them as the first child of el
5382          * @param {String/HTMLElement/Element} el The context element
5383          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5384          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5385          * @return {HTMLElement/Roo.Element} The new node
5386          */
5387         insertFirst : function(el, o, returnElement){
5388             return this.doInsert(el, o, returnElement, "afterBegin");
5389         },
5390     
5391         // private
5392         doInsert : function(el, o, returnElement, pos, sibling){
5393             el = Roo.getDom(el);
5394             var newNode;
5395             if(this.useDom || o.ns){
5396                 newNode = createDom(o, null);
5397                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5398             }else{
5399                 var html = createHtml(o);
5400                 newNode = this.insertHtml(pos, el, html);
5401             }
5402             return returnElement ? Roo.get(newNode, true) : newNode;
5403         },
5404     
5405         /**
5406          * Creates new Dom element(s) and appends them to el
5407          * @param {String/HTMLElement/Element} el The context element
5408          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5409          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5410          * @return {HTMLElement/Roo.Element} The new node
5411          */
5412         append : function(el, o, returnElement){
5413             el = Roo.getDom(el);
5414             var newNode;
5415             if(this.useDom || o.ns){
5416                 newNode = createDom(o, null);
5417                 el.appendChild(newNode);
5418             }else{
5419                 var html = createHtml(o);
5420                 newNode = this.insertHtml("beforeEnd", el, html);
5421             }
5422             return returnElement ? Roo.get(newNode, true) : newNode;
5423         },
5424     
5425         /**
5426          * Creates new Dom element(s) and overwrites the contents of el with them
5427          * @param {String/HTMLElement/Element} el The context element
5428          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5429          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5430          * @return {HTMLElement/Roo.Element} The new node
5431          */
5432         overwrite : function(el, o, returnElement)
5433         {
5434             el = Roo.getDom(el);
5435             if (o.ns) {
5436               
5437                 while (el.childNodes.length) {
5438                     el.removeChild(el.firstChild);
5439                 }
5440                 createDom(o, el);
5441             } else {
5442                 el.innerHTML = createHtml(o);   
5443             }
5444             
5445             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5446         },
5447     
5448         /**
5449          * Creates a new Roo.DomHelper.Template from the Dom object spec
5450          * @param {Object} o The Dom object spec (and children)
5451          * @return {Roo.DomHelper.Template} The new template
5452          */
5453         createTemplate : function(o){
5454             var html = createHtml(o);
5455             return new Roo.Template(html);
5456         },
5457          /**
5458          * Updates the first element with the spec from the o (replacing if necessary)
5459          * This iterates through the children, and updates attributes / children etc..
5460          * @param {String/HTMLElement/Element} el The context element
5461          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5462          */
5463         
5464         update : function(el, o)
5465         {
5466             updateNode(Roo.getDom(el), createDom(o));
5467             
5468         }
5469         
5470         
5471     };
5472 }();
5473 /*
5474  * Based on:
5475  * Ext JS Library 1.1.1
5476  * Copyright(c) 2006-2007, Ext JS, LLC.
5477  *
5478  * Originally Released Under LGPL - original licence link has changed is not relivant.
5479  *
5480  * Fork - LGPL
5481  * <script type="text/javascript">
5482  */
5483  
5484 /**
5485 * @class Roo.Template
5486 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5487 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5488 * Usage:
5489 <pre><code>
5490 var t = new Roo.Template({
5491     html :  '&lt;div name="{id}"&gt;' + 
5492         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5493         '&lt;/div&gt;',
5494     myformat: function (value, allValues) {
5495         return 'XX' + value;
5496     }
5497 });
5498 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5499 </code></pre>
5500 * For more information see this blog post with examples:
5501 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5502      - Create Elements using DOM, HTML fragments and Templates</a>. 
5503 * @constructor
5504 * @param {Object} cfg - Configuration object.
5505 */
5506 Roo.Template = function(cfg){
5507     // BC!
5508     if(cfg instanceof Array){
5509         cfg = cfg.join("");
5510     }else if(arguments.length > 1){
5511         cfg = Array.prototype.join.call(arguments, "");
5512     }
5513     
5514     
5515     if (typeof(cfg) == 'object') {
5516         Roo.apply(this,cfg)
5517     } else {
5518         // bc
5519         this.html = cfg;
5520     }
5521     if (this.url) {
5522         this.load();
5523     }
5524     
5525 };
5526 Roo.Template.prototype = {
5527     
5528     /**
5529      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5530      */
5531     onLoad : false,
5532     
5533     
5534     /**
5535      * @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..
5536      *                    it should be fixed so that template is observable...
5537      */
5538     url : false,
5539     /**
5540      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5541      */
5542     html : '',
5543     
5544     
5545     compiled : false,
5546     loaded : false,
5547     /**
5548      * Returns an HTML fragment of this template with the specified values applied.
5549      * @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'})
5550      * @return {String} The HTML fragment
5551      */
5552     
5553    
5554     
5555     applyTemplate : function(values){
5556         //Roo.log(["applyTemplate", values]);
5557         try {
5558            
5559             if(this.compiled){
5560                 return this.compiled(values);
5561             }
5562             var useF = this.disableFormats !== true;
5563             var fm = Roo.util.Format, tpl = this;
5564             var fn = function(m, name, format, args){
5565                 if(format && useF){
5566                     if(format.substr(0, 5) == "this."){
5567                         return tpl.call(format.substr(5), values[name], values);
5568                     }else{
5569                         if(args){
5570                             // quoted values are required for strings in compiled templates, 
5571                             // but for non compiled we need to strip them
5572                             // quoted reversed for jsmin
5573                             var re = /^\s*['"](.*)["']\s*$/;
5574                             args = args.split(',');
5575                             for(var i = 0, len = args.length; i < len; i++){
5576                                 args[i] = args[i].replace(re, "$1");
5577                             }
5578                             args = [values[name]].concat(args);
5579                         }else{
5580                             args = [values[name]];
5581                         }
5582                         return fm[format].apply(fm, args);
5583                     }
5584                 }else{
5585                     return values[name] !== undefined ? values[name] : "";
5586                 }
5587             };
5588             return this.html.replace(this.re, fn);
5589         } catch (e) {
5590             Roo.log(e);
5591             throw e;
5592         }
5593          
5594     },
5595     
5596     loading : false,
5597       
5598     load : function ()
5599     {
5600          
5601         if (this.loading) {
5602             return;
5603         }
5604         var _t = this;
5605         
5606         this.loading = true;
5607         this.compiled = false;
5608         
5609         var cx = new Roo.data.Connection();
5610         cx.request({
5611             url : this.url,
5612             method : 'GET',
5613             success : function (response) {
5614                 _t.loading = false;
5615                 _t.url = false;
5616                 
5617                 _t.set(response.responseText,true);
5618                 _t.loaded = true;
5619                 if (_t.onLoad) {
5620                     _t.onLoad();
5621                 }
5622              },
5623             failure : function(response) {
5624                 Roo.log("Template failed to load from " + _t.url);
5625                 _t.loading = false;
5626             }
5627         });
5628     },
5629
5630     /**
5631      * Sets the HTML used as the template and optionally compiles it.
5632      * @param {String} html
5633      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5634      * @return {Roo.Template} this
5635      */
5636     set : function(html, compile){
5637         this.html = html;
5638         this.compiled = false;
5639         if(compile){
5640             this.compile();
5641         }
5642         return this;
5643     },
5644     
5645     /**
5646      * True to disable format functions (defaults to false)
5647      * @type Boolean
5648      */
5649     disableFormats : false,
5650     
5651     /**
5652     * The regular expression used to match template variables 
5653     * @type RegExp
5654     * @property 
5655     */
5656     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5657     
5658     /**
5659      * Compiles the template into an internal function, eliminating the RegEx overhead.
5660      * @return {Roo.Template} this
5661      */
5662     compile : function(){
5663         var fm = Roo.util.Format;
5664         var useF = this.disableFormats !== true;
5665         var sep = Roo.isGecko ? "+" : ",";
5666         var fn = function(m, name, format, args){
5667             if(format && useF){
5668                 args = args ? ',' + args : "";
5669                 if(format.substr(0, 5) != "this."){
5670                     format = "fm." + format + '(';
5671                 }else{
5672                     format = 'this.call("'+ format.substr(5) + '", ';
5673                     args = ", values";
5674                 }
5675             }else{
5676                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5677             }
5678             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5679         };
5680         var body;
5681         // branched to use + in gecko and [].join() in others
5682         if(Roo.isGecko){
5683             body = "this.compiled = function(values){ return '" +
5684                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5685                     "';};";
5686         }else{
5687             body = ["this.compiled = function(values){ return ['"];
5688             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5689             body.push("'].join('');};");
5690             body = body.join('');
5691         }
5692         /**
5693          * eval:var:values
5694          * eval:var:fm
5695          */
5696         eval(body);
5697         return this;
5698     },
5699     
5700     // private function used to call members
5701     call : function(fnName, value, allValues){
5702         return this[fnName](value, allValues);
5703     },
5704     
5705     /**
5706      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5707      * @param {String/HTMLElement/Roo.Element} el The context element
5708      * @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'})
5709      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5710      * @return {HTMLElement/Roo.Element} The new node or Element
5711      */
5712     insertFirst: function(el, values, returnElement){
5713         return this.doInsert('afterBegin', el, values, returnElement);
5714     },
5715
5716     /**
5717      * Applies the supplied values to the template and inserts the new node(s) before el.
5718      * @param {String/HTMLElement/Roo.Element} el The context element
5719      * @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'})
5720      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5721      * @return {HTMLElement/Roo.Element} The new node or Element
5722      */
5723     insertBefore: function(el, values, returnElement){
5724         return this.doInsert('beforeBegin', el, values, returnElement);
5725     },
5726
5727     /**
5728      * Applies the supplied values to the template and inserts the new node(s) after el.
5729      * @param {String/HTMLElement/Roo.Element} el The context element
5730      * @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'})
5731      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5732      * @return {HTMLElement/Roo.Element} The new node or Element
5733      */
5734     insertAfter : function(el, values, returnElement){
5735         return this.doInsert('afterEnd', el, values, returnElement);
5736     },
5737     
5738     /**
5739      * Applies the supplied values to the template and appends the new node(s) to el.
5740      * @param {String/HTMLElement/Roo.Element} el The context element
5741      * @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'})
5742      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5743      * @return {HTMLElement/Roo.Element} The new node or Element
5744      */
5745     append : function(el, values, returnElement){
5746         return this.doInsert('beforeEnd', el, values, returnElement);
5747     },
5748
5749     doInsert : function(where, el, values, returnEl){
5750         el = Roo.getDom(el);
5751         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5752         return returnEl ? Roo.get(newNode, true) : newNode;
5753     },
5754
5755     /**
5756      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5757      * @param {String/HTMLElement/Roo.Element} el The context element
5758      * @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'})
5759      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5760      * @return {HTMLElement/Roo.Element} The new node or Element
5761      */
5762     overwrite : function(el, values, returnElement){
5763         el = Roo.getDom(el);
5764         el.innerHTML = this.applyTemplate(values);
5765         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5766     }
5767 };
5768 /**
5769  * Alias for {@link #applyTemplate}
5770  * @method
5771  */
5772 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5773
5774 // backwards compat
5775 Roo.DomHelper.Template = Roo.Template;
5776
5777 /**
5778  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5779  * @param {String/HTMLElement} el A DOM element or its id
5780  * @returns {Roo.Template} The created template
5781  * @static
5782  */
5783 Roo.Template.from = function(el){
5784     el = Roo.getDom(el);
5785     return new Roo.Template(el.value || el.innerHTML);
5786 };/*
5787  * Based on:
5788  * Ext JS Library 1.1.1
5789  * Copyright(c) 2006-2007, Ext JS, LLC.
5790  *
5791  * Originally Released Under LGPL - original licence link has changed is not relivant.
5792  *
5793  * Fork - LGPL
5794  * <script type="text/javascript">
5795  */
5796  
5797
5798 /*
5799  * This is code is also distributed under MIT license for use
5800  * with jQuery and prototype JavaScript libraries.
5801  */
5802 /**
5803  * @class Roo.DomQuery
5804 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).
5805 <p>
5806 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>
5807
5808 <p>
5809 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.
5810 </p>
5811 <h4>Element Selectors:</h4>
5812 <ul class="list">
5813     <li> <b>*</b> any element</li>
5814     <li> <b>E</b> an element with the tag E</li>
5815     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5816     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5817     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5818     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5819 </ul>
5820 <h4>Attribute Selectors:</h4>
5821 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5822 <ul class="list">
5823     <li> <b>E[foo]</b> has an attribute "foo"</li>
5824     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5825     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5826     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5827     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5828     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5829     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5830 </ul>
5831 <h4>Pseudo Classes:</h4>
5832 <ul class="list">
5833     <li> <b>E:first-child</b> E is the first child of its parent</li>
5834     <li> <b>E:last-child</b> E is the last child of its parent</li>
5835     <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>
5836     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5837     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5838     <li> <b>E:only-child</b> E is the only child of its parent</li>
5839     <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>
5840     <li> <b>E:first</b> the first E in the resultset</li>
5841     <li> <b>E:last</b> the last E in the resultset</li>
5842     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5843     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5844     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5845     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5846     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5847     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5848     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5849     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5850     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5851 </ul>
5852 <h4>CSS Value Selectors:</h4>
5853 <ul class="list">
5854     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5855     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5856     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5857     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5858     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5859     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5860 </ul>
5861  * @static
5862  */
5863 Roo.DomQuery = function(){
5864     var cache = {}, simpleCache = {}, valueCache = {};
5865     var nonSpace = /\S/;
5866     var trimRe = /^\s+|\s+$/g;
5867     var tplRe = /\{(\d+)\}/g;
5868     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5869     var tagTokenRe = /^(#)?([\w-\*]+)/;
5870     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5871
5872     function child(p, index){
5873         var i = 0;
5874         var n = p.firstChild;
5875         while(n){
5876             if(n.nodeType == 1){
5877                if(++i == index){
5878                    return n;
5879                }
5880             }
5881             n = n.nextSibling;
5882         }
5883         return null;
5884     };
5885
5886     function next(n){
5887         while((n = n.nextSibling) && n.nodeType != 1);
5888         return n;
5889     };
5890
5891     function prev(n){
5892         while((n = n.previousSibling) && n.nodeType != 1);
5893         return n;
5894     };
5895
5896     function children(d){
5897         var n = d.firstChild, ni = -1;
5898             while(n){
5899                 var nx = n.nextSibling;
5900                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5901                     d.removeChild(n);
5902                 }else{
5903                     n.nodeIndex = ++ni;
5904                 }
5905                 n = nx;
5906             }
5907             return this;
5908         };
5909
5910     function byClassName(c, a, v){
5911         if(!v){
5912             return c;
5913         }
5914         var r = [], ri = -1, cn;
5915         for(var i = 0, ci; ci = c[i]; i++){
5916             
5917             
5918             if((' '+
5919                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5920                  +' ').indexOf(v) != -1){
5921                 r[++ri] = ci;
5922             }
5923         }
5924         return r;
5925     };
5926
5927     function attrValue(n, attr){
5928         if(!n.tagName && typeof n.length != "undefined"){
5929             n = n[0];
5930         }
5931         if(!n){
5932             return null;
5933         }
5934         if(attr == "for"){
5935             return n.htmlFor;
5936         }
5937         if(attr == "class" || attr == "className"){
5938             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5939         }
5940         return n.getAttribute(attr) || n[attr];
5941
5942     };
5943
5944     function getNodes(ns, mode, tagName){
5945         var result = [], ri = -1, cs;
5946         if(!ns){
5947             return result;
5948         }
5949         tagName = tagName || "*";
5950         if(typeof ns.getElementsByTagName != "undefined"){
5951             ns = [ns];
5952         }
5953         if(!mode){
5954             for(var i = 0, ni; ni = ns[i]; i++){
5955                 cs = ni.getElementsByTagName(tagName);
5956                 for(var j = 0, ci; ci = cs[j]; j++){
5957                     result[++ri] = ci;
5958                 }
5959             }
5960         }else if(mode == "/" || mode == ">"){
5961             var utag = tagName.toUpperCase();
5962             for(var i = 0, ni, cn; ni = ns[i]; i++){
5963                 cn = ni.children || ni.childNodes;
5964                 for(var j = 0, cj; cj = cn[j]; j++){
5965                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5966                         result[++ri] = cj;
5967                     }
5968                 }
5969             }
5970         }else if(mode == "+"){
5971             var utag = tagName.toUpperCase();
5972             for(var i = 0, n; n = ns[i]; i++){
5973                 while((n = n.nextSibling) && n.nodeType != 1);
5974                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5975                     result[++ri] = n;
5976                 }
5977             }
5978         }else if(mode == "~"){
5979             for(var i = 0, n; n = ns[i]; i++){
5980                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5981                 if(n){
5982                     result[++ri] = n;
5983                 }
5984             }
5985         }
5986         return result;
5987     };
5988
5989     function concat(a, b){
5990         if(b.slice){
5991             return a.concat(b);
5992         }
5993         for(var i = 0, l = b.length; i < l; i++){
5994             a[a.length] = b[i];
5995         }
5996         return a;
5997     }
5998
5999     function byTag(cs, tagName){
6000         if(cs.tagName || cs == document){
6001             cs = [cs];
6002         }
6003         if(!tagName){
6004             return cs;
6005         }
6006         var r = [], ri = -1;
6007         tagName = tagName.toLowerCase();
6008         for(var i = 0, ci; ci = cs[i]; i++){
6009             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6010                 r[++ri] = ci;
6011             }
6012         }
6013         return r;
6014     };
6015
6016     function byId(cs, attr, id){
6017         if(cs.tagName || cs == document){
6018             cs = [cs];
6019         }
6020         if(!id){
6021             return cs;
6022         }
6023         var r = [], ri = -1;
6024         for(var i = 0,ci; ci = cs[i]; i++){
6025             if(ci && ci.id == id){
6026                 r[++ri] = ci;
6027                 return r;
6028             }
6029         }
6030         return r;
6031     };
6032
6033     function byAttribute(cs, attr, value, op, custom){
6034         var r = [], ri = -1, st = custom=="{";
6035         var f = Roo.DomQuery.operators[op];
6036         for(var i = 0, ci; ci = cs[i]; i++){
6037             var a;
6038             if(st){
6039                 a = Roo.DomQuery.getStyle(ci, attr);
6040             }
6041             else if(attr == "class" || attr == "className"){
6042                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6043             }else if(attr == "for"){
6044                 a = ci.htmlFor;
6045             }else if(attr == "href"){
6046                 a = ci.getAttribute("href", 2);
6047             }else{
6048                 a = ci.getAttribute(attr);
6049             }
6050             if((f && f(a, value)) || (!f && a)){
6051                 r[++ri] = ci;
6052             }
6053         }
6054         return r;
6055     };
6056
6057     function byPseudo(cs, name, value){
6058         return Roo.DomQuery.pseudos[name](cs, value);
6059     };
6060
6061     // This is for IE MSXML which does not support expandos.
6062     // IE runs the same speed using setAttribute, however FF slows way down
6063     // and Safari completely fails so they need to continue to use expandos.
6064     var isIE = window.ActiveXObject ? true : false;
6065
6066     // this eval is stop the compressor from
6067     // renaming the variable to something shorter
6068     
6069     /** eval:var:batch */
6070     var batch = 30803; 
6071
6072     var key = 30803;
6073
6074     function nodupIEXml(cs){
6075         var d = ++key;
6076         cs[0].setAttribute("_nodup", d);
6077         var r = [cs[0]];
6078         for(var i = 1, len = cs.length; i < len; i++){
6079             var c = cs[i];
6080             if(!c.getAttribute("_nodup") != d){
6081                 c.setAttribute("_nodup", d);
6082                 r[r.length] = c;
6083             }
6084         }
6085         for(var i = 0, len = cs.length; i < len; i++){
6086             cs[i].removeAttribute("_nodup");
6087         }
6088         return r;
6089     }
6090
6091     function nodup(cs){
6092         if(!cs){
6093             return [];
6094         }
6095         var len = cs.length, c, i, r = cs, cj, ri = -1;
6096         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6097             return cs;
6098         }
6099         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6100             return nodupIEXml(cs);
6101         }
6102         var d = ++key;
6103         cs[0]._nodup = d;
6104         for(i = 1; c = cs[i]; i++){
6105             if(c._nodup != d){
6106                 c._nodup = d;
6107             }else{
6108                 r = [];
6109                 for(var j = 0; j < i; j++){
6110                     r[++ri] = cs[j];
6111                 }
6112                 for(j = i+1; cj = cs[j]; j++){
6113                     if(cj._nodup != d){
6114                         cj._nodup = d;
6115                         r[++ri] = cj;
6116                     }
6117                 }
6118                 return r;
6119             }
6120         }
6121         return r;
6122     }
6123
6124     function quickDiffIEXml(c1, c2){
6125         var d = ++key;
6126         for(var i = 0, len = c1.length; i < len; i++){
6127             c1[i].setAttribute("_qdiff", d);
6128         }
6129         var r = [];
6130         for(var i = 0, len = c2.length; i < len; i++){
6131             if(c2[i].getAttribute("_qdiff") != d){
6132                 r[r.length] = c2[i];
6133             }
6134         }
6135         for(var i = 0, len = c1.length; i < len; i++){
6136            c1[i].removeAttribute("_qdiff");
6137         }
6138         return r;
6139     }
6140
6141     function quickDiff(c1, c2){
6142         var len1 = c1.length;
6143         if(!len1){
6144             return c2;
6145         }
6146         if(isIE && c1[0].selectSingleNode){
6147             return quickDiffIEXml(c1, c2);
6148         }
6149         var d = ++key;
6150         for(var i = 0; i < len1; i++){
6151             c1[i]._qdiff = d;
6152         }
6153         var r = [];
6154         for(var i = 0, len = c2.length; i < len; i++){
6155             if(c2[i]._qdiff != d){
6156                 r[r.length] = c2[i];
6157             }
6158         }
6159         return r;
6160     }
6161
6162     function quickId(ns, mode, root, id){
6163         if(ns == root){
6164            var d = root.ownerDocument || root;
6165            return d.getElementById(id);
6166         }
6167         ns = getNodes(ns, mode, "*");
6168         return byId(ns, null, id);
6169     }
6170
6171     return {
6172         getStyle : function(el, name){
6173             return Roo.fly(el).getStyle(name);
6174         },
6175         /**
6176          * Compiles a selector/xpath query into a reusable function. The returned function
6177          * takes one parameter "root" (optional), which is the context node from where the query should start.
6178          * @param {String} selector The selector/xpath query
6179          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6180          * @return {Function}
6181          */
6182         compile : function(path, type){
6183             type = type || "select";
6184             
6185             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6186             var q = path, mode, lq;
6187             var tk = Roo.DomQuery.matchers;
6188             var tklen = tk.length;
6189             var mm;
6190
6191             // accept leading mode switch
6192             var lmode = q.match(modeRe);
6193             if(lmode && lmode[1]){
6194                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6195                 q = q.replace(lmode[1], "");
6196             }
6197             // strip leading slashes
6198             while(path.substr(0, 1)=="/"){
6199                 path = path.substr(1);
6200             }
6201
6202             while(q && lq != q){
6203                 lq = q;
6204                 var tm = q.match(tagTokenRe);
6205                 if(type == "select"){
6206                     if(tm){
6207                         if(tm[1] == "#"){
6208                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6209                         }else{
6210                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6211                         }
6212                         q = q.replace(tm[0], "");
6213                     }else if(q.substr(0, 1) != '@'){
6214                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6215                     }
6216                 }else{
6217                     if(tm){
6218                         if(tm[1] == "#"){
6219                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6220                         }else{
6221                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6222                         }
6223                         q = q.replace(tm[0], "");
6224                     }
6225                 }
6226                 while(!(mm = q.match(modeRe))){
6227                     var matched = false;
6228                     for(var j = 0; j < tklen; j++){
6229                         var t = tk[j];
6230                         var m = q.match(t.re);
6231                         if(m){
6232                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6233                                                     return m[i];
6234                                                 });
6235                             q = q.replace(m[0], "");
6236                             matched = true;
6237                             break;
6238                         }
6239                     }
6240                     // prevent infinite loop on bad selector
6241                     if(!matched){
6242                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6243                     }
6244                 }
6245                 if(mm[1]){
6246                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6247                     q = q.replace(mm[1], "");
6248                 }
6249             }
6250             fn[fn.length] = "return nodup(n);\n}";
6251             
6252              /** 
6253               * list of variables that need from compression as they are used by eval.
6254              *  eval:var:batch 
6255              *  eval:var:nodup
6256              *  eval:var:byTag
6257              *  eval:var:ById
6258              *  eval:var:getNodes
6259              *  eval:var:quickId
6260              *  eval:var:mode
6261              *  eval:var:root
6262              *  eval:var:n
6263              *  eval:var:byClassName
6264              *  eval:var:byPseudo
6265              *  eval:var:byAttribute
6266              *  eval:var:attrValue
6267              * 
6268              **/ 
6269             eval(fn.join(""));
6270             return f;
6271         },
6272
6273         /**
6274          * Selects a group of elements.
6275          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6276          * @param {Node} root (optional) The start of the query (defaults to document).
6277          * @return {Array}
6278          */
6279         select : function(path, root, type){
6280             if(!root || root == document){
6281                 root = document;
6282             }
6283             if(typeof root == "string"){
6284                 root = document.getElementById(root);
6285             }
6286             var paths = path.split(",");
6287             var results = [];
6288             for(var i = 0, len = paths.length; i < len; i++){
6289                 var p = paths[i].replace(trimRe, "");
6290                 if(!cache[p]){
6291                     cache[p] = Roo.DomQuery.compile(p);
6292                     if(!cache[p]){
6293                         throw p + " is not a valid selector";
6294                     }
6295                 }
6296                 var result = cache[p](root);
6297                 if(result && result != document){
6298                     results = results.concat(result);
6299                 }
6300             }
6301             if(paths.length > 1){
6302                 return nodup(results);
6303             }
6304             return results;
6305         },
6306
6307         /**
6308          * Selects a single element.
6309          * @param {String} selector The selector/xpath query
6310          * @param {Node} root (optional) The start of the query (defaults to document).
6311          * @return {Element}
6312          */
6313         selectNode : function(path, root){
6314             return Roo.DomQuery.select(path, root)[0];
6315         },
6316
6317         /**
6318          * Selects the value of a node, optionally replacing null with the defaultValue.
6319          * @param {String} selector The selector/xpath query
6320          * @param {Node} root (optional) The start of the query (defaults to document).
6321          * @param {String} defaultValue
6322          */
6323         selectValue : function(path, root, defaultValue){
6324             path = path.replace(trimRe, "");
6325             if(!valueCache[path]){
6326                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6327             }
6328             var n = valueCache[path](root);
6329             n = n[0] ? n[0] : n;
6330             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6331             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6332         },
6333
6334         /**
6335          * Selects the value of a node, parsing integers and floats.
6336          * @param {String} selector The selector/xpath query
6337          * @param {Node} root (optional) The start of the query (defaults to document).
6338          * @param {Number} defaultValue
6339          * @return {Number}
6340          */
6341         selectNumber : function(path, root, defaultValue){
6342             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6343             return parseFloat(v);
6344         },
6345
6346         /**
6347          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6348          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6349          * @param {String} selector The simple selector to test
6350          * @return {Boolean}
6351          */
6352         is : function(el, ss){
6353             if(typeof el == "string"){
6354                 el = document.getElementById(el);
6355             }
6356             var isArray = (el instanceof Array);
6357             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6358             return isArray ? (result.length == el.length) : (result.length > 0);
6359         },
6360
6361         /**
6362          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6363          * @param {Array} el An array of elements to filter
6364          * @param {String} selector The simple selector to test
6365          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6366          * the selector instead of the ones that match
6367          * @return {Array}
6368          */
6369         filter : function(els, ss, nonMatches){
6370             ss = ss.replace(trimRe, "");
6371             if(!simpleCache[ss]){
6372                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6373             }
6374             var result = simpleCache[ss](els);
6375             return nonMatches ? quickDiff(result, els) : result;
6376         },
6377
6378         /**
6379          * Collection of matching regular expressions and code snippets.
6380          */
6381         matchers : [{
6382                 re: /^\.([\w-]+)/,
6383                 select: 'n = byClassName(n, null, " {1} ");'
6384             }, {
6385                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6386                 select: 'n = byPseudo(n, "{1}", "{2}");'
6387             },{
6388                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6389                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6390             }, {
6391                 re: /^#([\w-]+)/,
6392                 select: 'n = byId(n, null, "{1}");'
6393             },{
6394                 re: /^@([\w-]+)/,
6395                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6396             }
6397         ],
6398
6399         /**
6400          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6401          * 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;.
6402          */
6403         operators : {
6404             "=" : function(a, v){
6405                 return a == v;
6406             },
6407             "!=" : function(a, v){
6408                 return a != v;
6409             },
6410             "^=" : function(a, v){
6411                 return a && a.substr(0, v.length) == v;
6412             },
6413             "$=" : function(a, v){
6414                 return a && a.substr(a.length-v.length) == v;
6415             },
6416             "*=" : function(a, v){
6417                 return a && a.indexOf(v) !== -1;
6418             },
6419             "%=" : function(a, v){
6420                 return (a % v) == 0;
6421             },
6422             "|=" : function(a, v){
6423                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6424             },
6425             "~=" : function(a, v){
6426                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6427             }
6428         },
6429
6430         /**
6431          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6432          * and the argument (if any) supplied in the selector.
6433          */
6434         pseudos : {
6435             "first-child" : function(c){
6436                 var r = [], ri = -1, n;
6437                 for(var i = 0, ci; ci = n = c[i]; i++){
6438                     while((n = n.previousSibling) && n.nodeType != 1);
6439                     if(!n){
6440                         r[++ri] = ci;
6441                     }
6442                 }
6443                 return r;
6444             },
6445
6446             "last-child" : function(c){
6447                 var r = [], ri = -1, n;
6448                 for(var i = 0, ci; ci = n = c[i]; i++){
6449                     while((n = n.nextSibling) && n.nodeType != 1);
6450                     if(!n){
6451                         r[++ri] = ci;
6452                     }
6453                 }
6454                 return r;
6455             },
6456
6457             "nth-child" : function(c, a) {
6458                 var r = [], ri = -1;
6459                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6460                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6461                 for(var i = 0, n; n = c[i]; i++){
6462                     var pn = n.parentNode;
6463                     if (batch != pn._batch) {
6464                         var j = 0;
6465                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6466                             if(cn.nodeType == 1){
6467                                cn.nodeIndex = ++j;
6468                             }
6469                         }
6470                         pn._batch = batch;
6471                     }
6472                     if (f == 1) {
6473                         if (l == 0 || n.nodeIndex == l){
6474                             r[++ri] = n;
6475                         }
6476                     } else if ((n.nodeIndex + l) % f == 0){
6477                         r[++ri] = n;
6478                     }
6479                 }
6480
6481                 return r;
6482             },
6483
6484             "only-child" : function(c){
6485                 var r = [], ri = -1;;
6486                 for(var i = 0, ci; ci = c[i]; i++){
6487                     if(!prev(ci) && !next(ci)){
6488                         r[++ri] = ci;
6489                     }
6490                 }
6491                 return r;
6492             },
6493
6494             "empty" : function(c){
6495                 var r = [], ri = -1;
6496                 for(var i = 0, ci; ci = c[i]; i++){
6497                     var cns = ci.childNodes, j = 0, cn, empty = true;
6498                     while(cn = cns[j]){
6499                         ++j;
6500                         if(cn.nodeType == 1 || cn.nodeType == 3){
6501                             empty = false;
6502                             break;
6503                         }
6504                     }
6505                     if(empty){
6506                         r[++ri] = ci;
6507                     }
6508                 }
6509                 return r;
6510             },
6511
6512             "contains" : function(c, v){
6513                 var r = [], ri = -1;
6514                 for(var i = 0, ci; ci = c[i]; i++){
6515                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6516                         r[++ri] = ci;
6517                     }
6518                 }
6519                 return r;
6520             },
6521
6522             "nodeValue" : function(c, v){
6523                 var r = [], ri = -1;
6524                 for(var i = 0, ci; ci = c[i]; i++){
6525                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6526                         r[++ri] = ci;
6527                     }
6528                 }
6529                 return r;
6530             },
6531
6532             "checked" : function(c){
6533                 var r = [], ri = -1;
6534                 for(var i = 0, ci; ci = c[i]; i++){
6535                     if(ci.checked == true){
6536                         r[++ri] = ci;
6537                     }
6538                 }
6539                 return r;
6540             },
6541
6542             "not" : function(c, ss){
6543                 return Roo.DomQuery.filter(c, ss, true);
6544             },
6545
6546             "odd" : function(c){
6547                 return this["nth-child"](c, "odd");
6548             },
6549
6550             "even" : function(c){
6551                 return this["nth-child"](c, "even");
6552             },
6553
6554             "nth" : function(c, a){
6555                 return c[a-1] || [];
6556             },
6557
6558             "first" : function(c){
6559                 return c[0] || [];
6560             },
6561
6562             "last" : function(c){
6563                 return c[c.length-1] || [];
6564             },
6565
6566             "has" : function(c, ss){
6567                 var s = Roo.DomQuery.select;
6568                 var r = [], ri = -1;
6569                 for(var i = 0, ci; ci = c[i]; i++){
6570                     if(s(ss, ci).length > 0){
6571                         r[++ri] = ci;
6572                     }
6573                 }
6574                 return r;
6575             },
6576
6577             "next" : function(c, ss){
6578                 var is = Roo.DomQuery.is;
6579                 var r = [], ri = -1;
6580                 for(var i = 0, ci; ci = c[i]; i++){
6581                     var n = next(ci);
6582                     if(n && is(n, ss)){
6583                         r[++ri] = ci;
6584                     }
6585                 }
6586                 return r;
6587             },
6588
6589             "prev" : function(c, ss){
6590                 var is = Roo.DomQuery.is;
6591                 var r = [], ri = -1;
6592                 for(var i = 0, ci; ci = c[i]; i++){
6593                     var n = prev(ci);
6594                     if(n && is(n, ss)){
6595                         r[++ri] = ci;
6596                     }
6597                 }
6598                 return r;
6599             }
6600         }
6601     };
6602 }();
6603
6604 /**
6605  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6606  * @param {String} path The selector/xpath query
6607  * @param {Node} root (optional) The start of the query (defaults to document).
6608  * @return {Array}
6609  * @member Roo
6610  * @method query
6611  */
6612 Roo.query = Roo.DomQuery.select;
6613 /*
6614  * Based on:
6615  * Ext JS Library 1.1.1
6616  * Copyright(c) 2006-2007, Ext JS, LLC.
6617  *
6618  * Originally Released Under LGPL - original licence link has changed is not relivant.
6619  *
6620  * Fork - LGPL
6621  * <script type="text/javascript">
6622  */
6623
6624 /**
6625  * @class Roo.util.Observable
6626  * Base class that provides a common interface for publishing events. Subclasses are expected to
6627  * to have a property "events" with all the events defined.<br>
6628  * For example:
6629  * <pre><code>
6630  Employee = function(name){
6631     this.name = name;
6632     this.addEvents({
6633         "fired" : true,
6634         "quit" : true
6635     });
6636  }
6637  Roo.extend(Employee, Roo.util.Observable);
6638 </code></pre>
6639  * @param {Object} config properties to use (incuding events / listeners)
6640  */
6641
6642 Roo.util.Observable = function(cfg){
6643     
6644     cfg = cfg|| {};
6645     this.addEvents(cfg.events || {});
6646     if (cfg.events) {
6647         delete cfg.events; // make sure
6648     }
6649      
6650     Roo.apply(this, cfg);
6651     
6652     if(this.listeners){
6653         this.on(this.listeners);
6654         delete this.listeners;
6655     }
6656 };
6657 Roo.util.Observable.prototype = {
6658     /** 
6659  * @cfg {Object} listeners  list of events and functions to call for this object, 
6660  * For example :
6661  * <pre><code>
6662     listeners :  { 
6663        'click' : function(e) {
6664            ..... 
6665         } ,
6666         .... 
6667     } 
6668   </code></pre>
6669  */
6670     
6671     
6672     /**
6673      * Fires the specified event with the passed parameters (minus the event name).
6674      * @param {String} eventName
6675      * @param {Object...} args Variable number of parameters are passed to handlers
6676      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6677      */
6678     fireEvent : function(){
6679         var ce = this.events[arguments[0].toLowerCase()];
6680         if(typeof ce == "object"){
6681             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6682         }else{
6683             return true;
6684         }
6685     },
6686
6687     // private
6688     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6689
6690     /**
6691      * Appends an event handler to this component
6692      * @param {String}   eventName The type of event to listen for
6693      * @param {Function} handler The method the event invokes
6694      * @param {Object}   scope (optional) The scope in which to execute the handler
6695      * function. The handler function's "this" context.
6696      * @param {Object}   options (optional) An object containing handler configuration
6697      * properties. This may contain any of the following properties:<ul>
6698      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6699      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6700      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6701      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6702      * by the specified number of milliseconds. If the event fires again within that time, the original
6703      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6704      * </ul><br>
6705      * <p>
6706      * <b>Combining Options</b><br>
6707      * Using the options argument, it is possible to combine different types of listeners:<br>
6708      * <br>
6709      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6710                 <pre><code>
6711                 el.on('click', this.onClick, this, {
6712                         single: true,
6713                 delay: 100,
6714                 forumId: 4
6715                 });
6716                 </code></pre>
6717      * <p>
6718      * <b>Attaching multiple handlers in 1 call</b><br>
6719      * The method also allows for a single argument to be passed which is a config object containing properties
6720      * which specify multiple handlers.
6721      * <pre><code>
6722                 el.on({
6723                         'click': {
6724                         fn: this.onClick,
6725                         scope: this,
6726                         delay: 100
6727                 }, 
6728                 'mouseover': {
6729                         fn: this.onMouseOver,
6730                         scope: this
6731                 },
6732                 'mouseout': {
6733                         fn: this.onMouseOut,
6734                         scope: this
6735                 }
6736                 });
6737                 </code></pre>
6738      * <p>
6739      * Or a shorthand syntax which passes the same scope object to all handlers:
6740         <pre><code>
6741                 el.on({
6742                         'click': this.onClick,
6743                 'mouseover': this.onMouseOver,
6744                 'mouseout': this.onMouseOut,
6745                 scope: this
6746                 });
6747                 </code></pre>
6748      */
6749     addListener : function(eventName, fn, scope, o){
6750         if(typeof eventName == "object"){
6751             o = eventName;
6752             for(var e in o){
6753                 if(this.filterOptRe.test(e)){
6754                     continue;
6755                 }
6756                 if(typeof o[e] == "function"){
6757                     // shared options
6758                     this.addListener(e, o[e], o.scope,  o);
6759                 }else{
6760                     // individual options
6761                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6762                 }
6763             }
6764             return;
6765         }
6766         o = (!o || typeof o == "boolean") ? {} : o;
6767         eventName = eventName.toLowerCase();
6768         var ce = this.events[eventName] || true;
6769         if(typeof ce == "boolean"){
6770             ce = new Roo.util.Event(this, eventName);
6771             this.events[eventName] = ce;
6772         }
6773         ce.addListener(fn, scope, o);
6774     },
6775
6776     /**
6777      * Removes a listener
6778      * @param {String}   eventName     The type of event to listen for
6779      * @param {Function} handler        The handler to remove
6780      * @param {Object}   scope  (optional) The scope (this object) for the handler
6781      */
6782     removeListener : function(eventName, fn, scope){
6783         var ce = this.events[eventName.toLowerCase()];
6784         if(typeof ce == "object"){
6785             ce.removeListener(fn, scope);
6786         }
6787     },
6788
6789     /**
6790      * Removes all listeners for this object
6791      */
6792     purgeListeners : function(){
6793         for(var evt in this.events){
6794             if(typeof this.events[evt] == "object"){
6795                  this.events[evt].clearListeners();
6796             }
6797         }
6798     },
6799
6800     relayEvents : function(o, events){
6801         var createHandler = function(ename){
6802             return function(){
6803                  
6804                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6805             };
6806         };
6807         for(var i = 0, len = events.length; i < len; i++){
6808             var ename = events[i];
6809             if(!this.events[ename]){
6810                 this.events[ename] = true;
6811             };
6812             o.on(ename, createHandler(ename), this);
6813         }
6814     },
6815
6816     /**
6817      * Used to define events on this Observable
6818      * @param {Object} object The object with the events defined
6819      */
6820     addEvents : function(o){
6821         if(!this.events){
6822             this.events = {};
6823         }
6824         Roo.applyIf(this.events, o);
6825     },
6826
6827     /**
6828      * Checks to see if this object has any listeners for a specified event
6829      * @param {String} eventName The name of the event to check for
6830      * @return {Boolean} True if the event is being listened for, else false
6831      */
6832     hasListener : function(eventName){
6833         var e = this.events[eventName];
6834         return typeof e == "object" && e.listeners.length > 0;
6835     }
6836 };
6837 /**
6838  * Appends an event handler to this element (shorthand for addListener)
6839  * @param {String}   eventName     The type of event to listen for
6840  * @param {Function} handler        The method the event invokes
6841  * @param {Object}   scope (optional) The scope in which to execute the handler
6842  * function. The handler function's "this" context.
6843  * @param {Object}   options  (optional)
6844  * @method
6845  */
6846 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6847 /**
6848  * Removes a listener (shorthand for removeListener)
6849  * @param {String}   eventName     The type of event to listen for
6850  * @param {Function} handler        The handler to remove
6851  * @param {Object}   scope  (optional) The scope (this object) for the handler
6852  * @method
6853  */
6854 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6855
6856 /**
6857  * Starts capture on the specified Observable. All events will be passed
6858  * to the supplied function with the event name + standard signature of the event
6859  * <b>before</b> the event is fired. If the supplied function returns false,
6860  * the event will not fire.
6861  * @param {Observable} o The Observable to capture
6862  * @param {Function} fn The function to call
6863  * @param {Object} scope (optional) The scope (this object) for the fn
6864  * @static
6865  */
6866 Roo.util.Observable.capture = function(o, fn, scope){
6867     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6868 };
6869
6870 /**
6871  * Removes <b>all</b> added captures from the Observable.
6872  * @param {Observable} o The Observable to release
6873  * @static
6874  */
6875 Roo.util.Observable.releaseCapture = function(o){
6876     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6877 };
6878
6879 (function(){
6880
6881     var createBuffered = function(h, o, scope){
6882         var task = new Roo.util.DelayedTask();
6883         return function(){
6884             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6885         };
6886     };
6887
6888     var createSingle = function(h, e, fn, scope){
6889         return function(){
6890             e.removeListener(fn, scope);
6891             return h.apply(scope, arguments);
6892         };
6893     };
6894
6895     var createDelayed = function(h, o, scope){
6896         return function(){
6897             var args = Array.prototype.slice.call(arguments, 0);
6898             setTimeout(function(){
6899                 h.apply(scope, args);
6900             }, o.delay || 10);
6901         };
6902     };
6903
6904     Roo.util.Event = function(obj, name){
6905         this.name = name;
6906         this.obj = obj;
6907         this.listeners = [];
6908     };
6909
6910     Roo.util.Event.prototype = {
6911         addListener : function(fn, scope, options){
6912             var o = options || {};
6913             scope = scope || this.obj;
6914             if(!this.isListening(fn, scope)){
6915                 var l = {fn: fn, scope: scope, options: o};
6916                 var h = fn;
6917                 if(o.delay){
6918                     h = createDelayed(h, o, scope);
6919                 }
6920                 if(o.single){
6921                     h = createSingle(h, this, fn, scope);
6922                 }
6923                 if(o.buffer){
6924                     h = createBuffered(h, o, scope);
6925                 }
6926                 l.fireFn = h;
6927                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6928                     this.listeners.push(l);
6929                 }else{
6930                     this.listeners = this.listeners.slice(0);
6931                     this.listeners.push(l);
6932                 }
6933             }
6934         },
6935
6936         findListener : function(fn, scope){
6937             scope = scope || this.obj;
6938             var ls = this.listeners;
6939             for(var i = 0, len = ls.length; i < len; i++){
6940                 var l = ls[i];
6941                 if(l.fn == fn && l.scope == scope){
6942                     return i;
6943                 }
6944             }
6945             return -1;
6946         },
6947
6948         isListening : function(fn, scope){
6949             return this.findListener(fn, scope) != -1;
6950         },
6951
6952         removeListener : function(fn, scope){
6953             var index;
6954             if((index = this.findListener(fn, scope)) != -1){
6955                 if(!this.firing){
6956                     this.listeners.splice(index, 1);
6957                 }else{
6958                     this.listeners = this.listeners.slice(0);
6959                     this.listeners.splice(index, 1);
6960                 }
6961                 return true;
6962             }
6963             return false;
6964         },
6965
6966         clearListeners : function(){
6967             this.listeners = [];
6968         },
6969
6970         fire : function(){
6971             var ls = this.listeners, scope, len = ls.length;
6972             if(len > 0){
6973                 this.firing = true;
6974                 var args = Array.prototype.slice.call(arguments, 0);                
6975                 for(var i = 0; i < len; i++){
6976                     var l = ls[i];
6977                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6978                         this.firing = false;
6979                         return false;
6980                     }
6981                 }
6982                 this.firing = false;
6983             }
6984             return true;
6985         }
6986     };
6987 })();/*
6988  * RooJS Library 
6989  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6990  *
6991  * Licence LGPL 
6992  *
6993  */
6994  
6995 /**
6996  * @class Roo.Document
6997  * @extends Roo.util.Observable
6998  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6999  * 
7000  * @param {Object} config the methods and properties of the 'base' class for the application.
7001  * 
7002  *  Generic Page handler - implement this to start your app..
7003  * 
7004  * eg.
7005  *  MyProject = new Roo.Document({
7006         events : {
7007             'load' : true // your events..
7008         },
7009         listeners : {
7010             'ready' : function() {
7011                 // fired on Roo.onReady()
7012             }
7013         }
7014  * 
7015  */
7016 Roo.Document = function(cfg) {
7017      
7018     this.addEvents({ 
7019         'ready' : true
7020     });
7021     Roo.util.Observable.call(this,cfg);
7022     
7023     var _this = this;
7024     
7025     Roo.onReady(function() {
7026         _this.fireEvent('ready');
7027     },null,false);
7028     
7029     
7030 }
7031
7032 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7033  * Based on:
7034  * Ext JS Library 1.1.1
7035  * Copyright(c) 2006-2007, Ext JS, LLC.
7036  *
7037  * Originally Released Under LGPL - original licence link has changed is not relivant.
7038  *
7039  * Fork - LGPL
7040  * <script type="text/javascript">
7041  */
7042
7043 /**
7044  * @class Roo.EventManager
7045  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7046  * several useful events directly.
7047  * See {@link Roo.EventObject} for more details on normalized event objects.
7048  * @static
7049  */
7050 Roo.EventManager = function(){
7051     var docReadyEvent, docReadyProcId, docReadyState = false;
7052     var resizeEvent, resizeTask, textEvent, textSize;
7053     var E = Roo.lib.Event;
7054     var D = Roo.lib.Dom;
7055
7056     
7057     
7058
7059     var fireDocReady = function(){
7060         if(!docReadyState){
7061             docReadyState = true;
7062             Roo.isReady = true;
7063             if(docReadyProcId){
7064                 clearInterval(docReadyProcId);
7065             }
7066             if(Roo.isGecko || Roo.isOpera) {
7067                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7068             }
7069             if(Roo.isIE){
7070                 var defer = document.getElementById("ie-deferred-loader");
7071                 if(defer){
7072                     defer.onreadystatechange = null;
7073                     defer.parentNode.removeChild(defer);
7074                 }
7075             }
7076             if(docReadyEvent){
7077                 docReadyEvent.fire();
7078                 docReadyEvent.clearListeners();
7079             }
7080         }
7081     };
7082     
7083     var initDocReady = function(){
7084         docReadyEvent = new Roo.util.Event();
7085         if(Roo.isGecko || Roo.isOpera) {
7086             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7087         }else if(Roo.isIE){
7088             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7089             var defer = document.getElementById("ie-deferred-loader");
7090             defer.onreadystatechange = function(){
7091                 if(this.readyState == "complete"){
7092                     fireDocReady();
7093                 }
7094             };
7095         }else if(Roo.isSafari){ 
7096             docReadyProcId = setInterval(function(){
7097                 var rs = document.readyState;
7098                 if(rs == "complete") {
7099                     fireDocReady();     
7100                  }
7101             }, 10);
7102         }
7103         // no matter what, make sure it fires on load
7104         E.on(window, "load", fireDocReady);
7105     };
7106
7107     var createBuffered = function(h, o){
7108         var task = new Roo.util.DelayedTask(h);
7109         return function(e){
7110             // create new event object impl so new events don't wipe out properties
7111             e = new Roo.EventObjectImpl(e);
7112             task.delay(o.buffer, h, null, [e]);
7113         };
7114     };
7115
7116     var createSingle = function(h, el, ename, fn){
7117         return function(e){
7118             Roo.EventManager.removeListener(el, ename, fn);
7119             h(e);
7120         };
7121     };
7122
7123     var createDelayed = function(h, o){
7124         return function(e){
7125             // create new event object impl so new events don't wipe out properties
7126             e = new Roo.EventObjectImpl(e);
7127             setTimeout(function(){
7128                 h(e);
7129             }, o.delay || 10);
7130         };
7131     };
7132     var transitionEndVal = false;
7133     
7134     var transitionEnd = function()
7135     {
7136         if (transitionEndVal) {
7137             return transitionEndVal;
7138         }
7139         var el = document.createElement('div');
7140
7141         var transEndEventNames = {
7142             WebkitTransition : 'webkitTransitionEnd',
7143             MozTransition    : 'transitionend',
7144             OTransition      : 'oTransitionEnd otransitionend',
7145             transition       : 'transitionend'
7146         };
7147     
7148         for (var name in transEndEventNames) {
7149             if (el.style[name] !== undefined) {
7150                 transitionEndVal = transEndEventNames[name];
7151                 return  transitionEndVal ;
7152             }
7153         }
7154     }
7155     
7156   
7157
7158     var listen = function(element, ename, opt, fn, scope)
7159     {
7160         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7161         fn = fn || o.fn; scope = scope || o.scope;
7162         var el = Roo.getDom(element);
7163         
7164         
7165         if(!el){
7166             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7167         }
7168         
7169         if (ename == 'transitionend') {
7170             ename = transitionEnd();
7171         }
7172         var h = function(e){
7173             e = Roo.EventObject.setEvent(e);
7174             var t;
7175             if(o.delegate){
7176                 t = e.getTarget(o.delegate, el);
7177                 if(!t){
7178                     return;
7179                 }
7180             }else{
7181                 t = e.target;
7182             }
7183             if(o.stopEvent === true){
7184                 e.stopEvent();
7185             }
7186             if(o.preventDefault === true){
7187                e.preventDefault();
7188             }
7189             if(o.stopPropagation === true){
7190                 e.stopPropagation();
7191             }
7192
7193             if(o.normalized === false){
7194                 e = e.browserEvent;
7195             }
7196
7197             fn.call(scope || el, e, t, o);
7198         };
7199         if(o.delay){
7200             h = createDelayed(h, o);
7201         }
7202         if(o.single){
7203             h = createSingle(h, el, ename, fn);
7204         }
7205         if(o.buffer){
7206             h = createBuffered(h, o);
7207         }
7208         
7209         fn._handlers = fn._handlers || [];
7210         
7211         
7212         fn._handlers.push([Roo.id(el), ename, h]);
7213         
7214         
7215          
7216         E.on(el, ename, h); // this adds the actuall listener to the object..
7217         
7218         
7219         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7220             el.addEventListener("DOMMouseScroll", h, false);
7221             E.on(window, 'unload', function(){
7222                 el.removeEventListener("DOMMouseScroll", h, false);
7223             });
7224         }
7225         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7226             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7227         }
7228         return h;
7229     };
7230
7231     var stopListening = function(el, ename, fn){
7232         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7233         if(hds){
7234             for(var i = 0, len = hds.length; i < len; i++){
7235                 var h = hds[i];
7236                 if(h[0] == id && h[1] == ename){
7237                     hd = h[2];
7238                     hds.splice(i, 1);
7239                     break;
7240                 }
7241             }
7242         }
7243         E.un(el, ename, hd);
7244         el = Roo.getDom(el);
7245         if(ename == "mousewheel" && el.addEventListener){
7246             el.removeEventListener("DOMMouseScroll", hd, false);
7247         }
7248         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7249             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7250         }
7251     };
7252
7253     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7254     
7255     var pub = {
7256         
7257         
7258         /** 
7259          * Fix for doc tools
7260          * @scope Roo.EventManager
7261          */
7262         
7263         
7264         /** 
7265          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7266          * object with a Roo.EventObject
7267          * @param {Function} fn        The method the event invokes
7268          * @param {Object}   scope    An object that becomes the scope of the handler
7269          * @param {boolean}  override If true, the obj passed in becomes
7270          *                             the execution scope of the listener
7271          * @return {Function} The wrapped function
7272          * @deprecated
7273          */
7274         wrap : function(fn, scope, override){
7275             return function(e){
7276                 Roo.EventObject.setEvent(e);
7277                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7278             };
7279         },
7280         
7281         /**
7282      * Appends an event handler to an element (shorthand for addListener)
7283      * @param {String/HTMLElement}   element        The html element or id to assign the
7284      * @param {String}   eventName The type of event to listen for
7285      * @param {Function} handler The method the event invokes
7286      * @param {Object}   scope (optional) The scope in which to execute the handler
7287      * function. The handler function's "this" context.
7288      * @param {Object}   options (optional) An object containing handler configuration
7289      * properties. This may contain any of the following properties:<ul>
7290      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7291      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7292      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7293      * <li>preventDefault {Boolean} True to prevent the default action</li>
7294      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7295      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7296      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7297      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7298      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7299      * by the specified number of milliseconds. If the event fires again within that time, the original
7300      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7301      * </ul><br>
7302      * <p>
7303      * <b>Combining Options</b><br>
7304      * Using the options argument, it is possible to combine different types of listeners:<br>
7305      * <br>
7306      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7307      * Code:<pre><code>
7308 el.on('click', this.onClick, this, {
7309     single: true,
7310     delay: 100,
7311     stopEvent : true,
7312     forumId: 4
7313 });</code></pre>
7314      * <p>
7315      * <b>Attaching multiple handlers in 1 call</b><br>
7316       * The method also allows for a single argument to be passed which is a config object containing properties
7317      * which specify multiple handlers.
7318      * <p>
7319      * Code:<pre><code>
7320 el.on({
7321     'click' : {
7322         fn: this.onClick
7323         scope: this,
7324         delay: 100
7325     },
7326     'mouseover' : {
7327         fn: this.onMouseOver
7328         scope: this
7329     },
7330     'mouseout' : {
7331         fn: this.onMouseOut
7332         scope: this
7333     }
7334 });</code></pre>
7335      * <p>
7336      * Or a shorthand syntax:<br>
7337      * Code:<pre><code>
7338 el.on({
7339     'click' : this.onClick,
7340     'mouseover' : this.onMouseOver,
7341     'mouseout' : this.onMouseOut
7342     scope: this
7343 });</code></pre>
7344      */
7345         addListener : function(element, eventName, fn, scope, options){
7346             if(typeof eventName == "object"){
7347                 var o = eventName;
7348                 for(var e in o){
7349                     if(propRe.test(e)){
7350                         continue;
7351                     }
7352                     if(typeof o[e] == "function"){
7353                         // shared options
7354                         listen(element, e, o, o[e], o.scope);
7355                     }else{
7356                         // individual options
7357                         listen(element, e, o[e]);
7358                     }
7359                 }
7360                 return;
7361             }
7362             return listen(element, eventName, options, fn, scope);
7363         },
7364         
7365         /**
7366          * Removes an event handler
7367          *
7368          * @param {String/HTMLElement}   element        The id or html element to remove the 
7369          *                             event from
7370          * @param {String}   eventName     The type of event
7371          * @param {Function} fn
7372          * @return {Boolean} True if a listener was actually removed
7373          */
7374         removeListener : function(element, eventName, fn){
7375             return stopListening(element, eventName, fn);
7376         },
7377         
7378         /**
7379          * Fires when the document is ready (before onload and before images are loaded). Can be 
7380          * accessed shorthanded Roo.onReady().
7381          * @param {Function} fn        The method the event invokes
7382          * @param {Object}   scope    An  object that becomes the scope of the handler
7383          * @param {boolean}  options
7384          */
7385         onDocumentReady : function(fn, scope, options){
7386             if(docReadyState){ // if it already fired
7387                 docReadyEvent.addListener(fn, scope, options);
7388                 docReadyEvent.fire();
7389                 docReadyEvent.clearListeners();
7390                 return;
7391             }
7392             if(!docReadyEvent){
7393                 initDocReady();
7394             }
7395             docReadyEvent.addListener(fn, scope, options);
7396         },
7397         
7398         /**
7399          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7400          * @param {Function} fn        The method the event invokes
7401          * @param {Object}   scope    An object that becomes the scope of the handler
7402          * @param {boolean}  options
7403          */
7404         onWindowResize : function(fn, scope, options)
7405         {
7406             if(!resizeEvent){
7407                 resizeEvent = new Roo.util.Event();
7408                 resizeTask = new Roo.util.DelayedTask(function(){
7409                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7410                 });
7411                 E.on(window, "resize", function()
7412                 {
7413                     if (Roo.isIE) {
7414                         resizeTask.delay(50);
7415                     } else {
7416                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7417                     }
7418                 });
7419             }
7420             resizeEvent.addListener(fn, scope, options);
7421         },
7422
7423         /**
7424          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7425          * @param {Function} fn        The method the event invokes
7426          * @param {Object}   scope    An object that becomes the scope of the handler
7427          * @param {boolean}  options
7428          */
7429         onTextResize : function(fn, scope, options){
7430             if(!textEvent){
7431                 textEvent = new Roo.util.Event();
7432                 var textEl = new Roo.Element(document.createElement('div'));
7433                 textEl.dom.className = 'x-text-resize';
7434                 textEl.dom.innerHTML = 'X';
7435                 textEl.appendTo(document.body);
7436                 textSize = textEl.dom.offsetHeight;
7437                 setInterval(function(){
7438                     if(textEl.dom.offsetHeight != textSize){
7439                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7440                     }
7441                 }, this.textResizeInterval);
7442             }
7443             textEvent.addListener(fn, scope, options);
7444         },
7445
7446         /**
7447          * Removes the passed window resize listener.
7448          * @param {Function} fn        The method the event invokes
7449          * @param {Object}   scope    The scope of handler
7450          */
7451         removeResizeListener : function(fn, scope){
7452             if(resizeEvent){
7453                 resizeEvent.removeListener(fn, scope);
7454             }
7455         },
7456
7457         // private
7458         fireResize : function(){
7459             if(resizeEvent){
7460                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7461             }   
7462         },
7463         /**
7464          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7465          */
7466         ieDeferSrc : false,
7467         /**
7468          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7469          */
7470         textResizeInterval : 50
7471     };
7472     
7473     /**
7474      * Fix for doc tools
7475      * @scopeAlias pub=Roo.EventManager
7476      */
7477     
7478      /**
7479      * Appends an event handler to an element (shorthand for addListener)
7480      * @param {String/HTMLElement}   element        The html element or id to assign the
7481      * @param {String}   eventName The type of event to listen for
7482      * @param {Function} handler The method the event invokes
7483      * @param {Object}   scope (optional) The scope in which to execute the handler
7484      * function. The handler function's "this" context.
7485      * @param {Object}   options (optional) An object containing handler configuration
7486      * properties. This may contain any of the following properties:<ul>
7487      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7488      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7489      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7490      * <li>preventDefault {Boolean} True to prevent the default action</li>
7491      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7492      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7493      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7494      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7495      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7496      * by the specified number of milliseconds. If the event fires again within that time, the original
7497      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7498      * </ul><br>
7499      * <p>
7500      * <b>Combining Options</b><br>
7501      * Using the options argument, it is possible to combine different types of listeners:<br>
7502      * <br>
7503      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7504      * Code:<pre><code>
7505 el.on('click', this.onClick, this, {
7506     single: true,
7507     delay: 100,
7508     stopEvent : true,
7509     forumId: 4
7510 });</code></pre>
7511      * <p>
7512      * <b>Attaching multiple handlers in 1 call</b><br>
7513       * The method also allows for a single argument to be passed which is a config object containing properties
7514      * which specify multiple handlers.
7515      * <p>
7516      * Code:<pre><code>
7517 el.on({
7518     'click' : {
7519         fn: this.onClick
7520         scope: this,
7521         delay: 100
7522     },
7523     'mouseover' : {
7524         fn: this.onMouseOver
7525         scope: this
7526     },
7527     'mouseout' : {
7528         fn: this.onMouseOut
7529         scope: this
7530     }
7531 });</code></pre>
7532      * <p>
7533      * Or a shorthand syntax:<br>
7534      * Code:<pre><code>
7535 el.on({
7536     'click' : this.onClick,
7537     'mouseover' : this.onMouseOver,
7538     'mouseout' : this.onMouseOut
7539     scope: this
7540 });</code></pre>
7541      */
7542     pub.on = pub.addListener;
7543     pub.un = pub.removeListener;
7544
7545     pub.stoppedMouseDownEvent = new Roo.util.Event();
7546     return pub;
7547 }();
7548 /**
7549   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7550   * @param {Function} fn        The method the event invokes
7551   * @param {Object}   scope    An  object that becomes the scope of the handler
7552   * @param {boolean}  override If true, the obj passed in becomes
7553   *                             the execution scope of the listener
7554   * @member Roo
7555   * @method onReady
7556  */
7557 Roo.onReady = Roo.EventManager.onDocumentReady;
7558
7559 Roo.onReady(function(){
7560     var bd = Roo.get(document.body);
7561     if(!bd){ return; }
7562
7563     var cls = [
7564             Roo.isIE ? "roo-ie"
7565             : Roo.isIE11 ? "roo-ie11"
7566             : Roo.isEdge ? "roo-edge"
7567             : Roo.isGecko ? "roo-gecko"
7568             : Roo.isOpera ? "roo-opera"
7569             : Roo.isSafari ? "roo-safari" : ""];
7570
7571     if(Roo.isMac){
7572         cls.push("roo-mac");
7573     }
7574     if(Roo.isLinux){
7575         cls.push("roo-linux");
7576     }
7577     if(Roo.isIOS){
7578         cls.push("roo-ios");
7579     }
7580     if(Roo.isTouch){
7581         cls.push("roo-touch");
7582     }
7583     if(Roo.isBorderBox){
7584         cls.push('roo-border-box');
7585     }
7586     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7587         var p = bd.dom.parentNode;
7588         if(p){
7589             p.className += ' roo-strict';
7590         }
7591     }
7592     bd.addClass(cls.join(' '));
7593 });
7594
7595 /**
7596  * @class Roo.EventObject
7597  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7598  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7599  * Example:
7600  * <pre><code>
7601  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7602     e.preventDefault();
7603     var target = e.getTarget();
7604     ...
7605  }
7606  var myDiv = Roo.get("myDiv");
7607  myDiv.on("click", handleClick);
7608  //or
7609  Roo.EventManager.on("myDiv", 'click', handleClick);
7610  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7611  </code></pre>
7612  * @static
7613  */
7614 Roo.EventObject = function(){
7615     
7616     var E = Roo.lib.Event;
7617     
7618     // safari keypress events for special keys return bad keycodes
7619     var safariKeys = {
7620         63234 : 37, // left
7621         63235 : 39, // right
7622         63232 : 38, // up
7623         63233 : 40, // down
7624         63276 : 33, // page up
7625         63277 : 34, // page down
7626         63272 : 46, // delete
7627         63273 : 36, // home
7628         63275 : 35  // end
7629     };
7630
7631     // normalize button clicks
7632     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7633                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7634
7635     Roo.EventObjectImpl = function(e){
7636         if(e){
7637             this.setEvent(e.browserEvent || e);
7638         }
7639     };
7640     Roo.EventObjectImpl.prototype = {
7641         /**
7642          * Used to fix doc tools.
7643          * @scope Roo.EventObject.prototype
7644          */
7645             
7646
7647         
7648         
7649         /** The normal browser event */
7650         browserEvent : null,
7651         /** The button pressed in a mouse event */
7652         button : -1,
7653         /** True if the shift key was down during the event */
7654         shiftKey : false,
7655         /** True if the control key was down during the event */
7656         ctrlKey : false,
7657         /** True if the alt key was down during the event */
7658         altKey : false,
7659
7660         /** Key constant 
7661         * @type Number */
7662         BACKSPACE : 8,
7663         /** Key constant 
7664         * @type Number */
7665         TAB : 9,
7666         /** Key constant 
7667         * @type Number */
7668         RETURN : 13,
7669         /** Key constant 
7670         * @type Number */
7671         ENTER : 13,
7672         /** Key constant 
7673         * @type Number */
7674         SHIFT : 16,
7675         /** Key constant 
7676         * @type Number */
7677         CONTROL : 17,
7678         /** Key constant 
7679         * @type Number */
7680         ESC : 27,
7681         /** Key constant 
7682         * @type Number */
7683         SPACE : 32,
7684         /** Key constant 
7685         * @type Number */
7686         PAGEUP : 33,
7687         /** Key constant 
7688         * @type Number */
7689         PAGEDOWN : 34,
7690         /** Key constant 
7691         * @type Number */
7692         END : 35,
7693         /** Key constant 
7694         * @type Number */
7695         HOME : 36,
7696         /** Key constant 
7697         * @type Number */
7698         LEFT : 37,
7699         /** Key constant 
7700         * @type Number */
7701         UP : 38,
7702         /** Key constant 
7703         * @type Number */
7704         RIGHT : 39,
7705         /** Key constant 
7706         * @type Number */
7707         DOWN : 40,
7708         /** Key constant 
7709         * @type Number */
7710         DELETE : 46,
7711         /** Key constant 
7712         * @type Number */
7713         F5 : 116,
7714
7715            /** @private */
7716         setEvent : function(e){
7717             if(e == this || (e && e.browserEvent)){ // already wrapped
7718                 return e;
7719             }
7720             this.browserEvent = e;
7721             if(e){
7722                 // normalize buttons
7723                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7724                 if(e.type == 'click' && this.button == -1){
7725                     this.button = 0;
7726                 }
7727                 this.type = e.type;
7728                 this.shiftKey = e.shiftKey;
7729                 // mac metaKey behaves like ctrlKey
7730                 this.ctrlKey = e.ctrlKey || e.metaKey;
7731                 this.altKey = e.altKey;
7732                 // in getKey these will be normalized for the mac
7733                 this.keyCode = e.keyCode;
7734                 // keyup warnings on firefox.
7735                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7736                 // cache the target for the delayed and or buffered events
7737                 this.target = E.getTarget(e);
7738                 // same for XY
7739                 this.xy = E.getXY(e);
7740             }else{
7741                 this.button = -1;
7742                 this.shiftKey = false;
7743                 this.ctrlKey = false;
7744                 this.altKey = false;
7745                 this.keyCode = 0;
7746                 this.charCode =0;
7747                 this.target = null;
7748                 this.xy = [0, 0];
7749             }
7750             return this;
7751         },
7752
7753         /**
7754          * Stop the event (preventDefault and stopPropagation)
7755          */
7756         stopEvent : function(){
7757             if(this.browserEvent){
7758                 if(this.browserEvent.type == 'mousedown'){
7759                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7760                 }
7761                 E.stopEvent(this.browserEvent);
7762             }
7763         },
7764
7765         /**
7766          * Prevents the browsers default handling of the event.
7767          */
7768         preventDefault : function(){
7769             if(this.browserEvent){
7770                 E.preventDefault(this.browserEvent);
7771             }
7772         },
7773
7774         /** @private */
7775         isNavKeyPress : function(){
7776             var k = this.keyCode;
7777             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7778             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7779         },
7780
7781         isSpecialKey : function(){
7782             var k = this.keyCode;
7783             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7784             (k == 16) || (k == 17) ||
7785             (k >= 18 && k <= 20) ||
7786             (k >= 33 && k <= 35) ||
7787             (k >= 36 && k <= 39) ||
7788             (k >= 44 && k <= 45);
7789         },
7790         /**
7791          * Cancels bubbling of the event.
7792          */
7793         stopPropagation : function(){
7794             if(this.browserEvent){
7795                 if(this.type == 'mousedown'){
7796                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7797                 }
7798                 E.stopPropagation(this.browserEvent);
7799             }
7800         },
7801
7802         /**
7803          * Gets the key code for the event.
7804          * @return {Number}
7805          */
7806         getCharCode : function(){
7807             return this.charCode || this.keyCode;
7808         },
7809
7810         /**
7811          * Returns a normalized keyCode for the event.
7812          * @return {Number} The key code
7813          */
7814         getKey : function(){
7815             var k = this.keyCode || this.charCode;
7816             return Roo.isSafari ? (safariKeys[k] || k) : k;
7817         },
7818
7819         /**
7820          * Gets the x coordinate of the event.
7821          * @return {Number}
7822          */
7823         getPageX : function(){
7824             return this.xy[0];
7825         },
7826
7827         /**
7828          * Gets the y coordinate of the event.
7829          * @return {Number}
7830          */
7831         getPageY : function(){
7832             return this.xy[1];
7833         },
7834
7835         /**
7836          * Gets the time of the event.
7837          * @return {Number}
7838          */
7839         getTime : function(){
7840             if(this.browserEvent){
7841                 return E.getTime(this.browserEvent);
7842             }
7843             return null;
7844         },
7845
7846         /**
7847          * Gets the page coordinates of the event.
7848          * @return {Array} The xy values like [x, y]
7849          */
7850         getXY : function(){
7851             return this.xy;
7852         },
7853
7854         /**
7855          * Gets the target for the event.
7856          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7857          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7858                 search as a number or element (defaults to 10 || document.body)
7859          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7860          * @return {HTMLelement}
7861          */
7862         getTarget : function(selector, maxDepth, returnEl){
7863             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7864         },
7865         /**
7866          * Gets the related target.
7867          * @return {HTMLElement}
7868          */
7869         getRelatedTarget : function(){
7870             if(this.browserEvent){
7871                 return E.getRelatedTarget(this.browserEvent);
7872             }
7873             return null;
7874         },
7875
7876         /**
7877          * Normalizes mouse wheel delta across browsers
7878          * @return {Number} The delta
7879          */
7880         getWheelDelta : function(){
7881             var e = this.browserEvent;
7882             var delta = 0;
7883             if(e.wheelDelta){ /* IE/Opera. */
7884                 delta = e.wheelDelta/120;
7885             }else if(e.detail){ /* Mozilla case. */
7886                 delta = -e.detail/3;
7887             }
7888             return delta;
7889         },
7890
7891         /**
7892          * Returns true if the control, meta, shift or alt key was pressed during this event.
7893          * @return {Boolean}
7894          */
7895         hasModifier : function(){
7896             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7897         },
7898
7899         /**
7900          * Returns true if the target of this event equals el or is a child of el
7901          * @param {String/HTMLElement/Element} el
7902          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7903          * @return {Boolean}
7904          */
7905         within : function(el, related){
7906             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7907             return t && Roo.fly(el).contains(t);
7908         },
7909
7910         getPoint : function(){
7911             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7912         }
7913     };
7914
7915     return new Roo.EventObjectImpl();
7916 }();
7917             
7918     /*
7919  * Based on:
7920  * Ext JS Library 1.1.1
7921  * Copyright(c) 2006-2007, Ext JS, LLC.
7922  *
7923  * Originally Released Under LGPL - original licence link has changed is not relivant.
7924  *
7925  * Fork - LGPL
7926  * <script type="text/javascript">
7927  */
7928
7929  
7930 // was in Composite Element!??!?!
7931  
7932 (function(){
7933     var D = Roo.lib.Dom;
7934     var E = Roo.lib.Event;
7935     var A = Roo.lib.Anim;
7936
7937     // local style camelizing for speed
7938     var propCache = {};
7939     var camelRe = /(-[a-z])/gi;
7940     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7941     var view = document.defaultView;
7942
7943 /**
7944  * @class Roo.Element
7945  * Represents an Element in the DOM.<br><br>
7946  * Usage:<br>
7947 <pre><code>
7948 var el = Roo.get("my-div");
7949
7950 // or with getEl
7951 var el = getEl("my-div");
7952
7953 // or with a DOM element
7954 var el = Roo.get(myDivElement);
7955 </code></pre>
7956  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7957  * each call instead of constructing a new one.<br><br>
7958  * <b>Animations</b><br />
7959  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7960  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7961 <pre>
7962 Option    Default   Description
7963 --------- --------  ---------------------------------------------
7964 duration  .35       The duration of the animation in seconds
7965 easing    easeOut   The YUI easing method
7966 callback  none      A function to execute when the anim completes
7967 scope     this      The scope (this) of the callback function
7968 </pre>
7969 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7970 * manipulate the animation. Here's an example:
7971 <pre><code>
7972 var el = Roo.get("my-div");
7973
7974 // no animation
7975 el.setWidth(100);
7976
7977 // default animation
7978 el.setWidth(100, true);
7979
7980 // animation with some options set
7981 el.setWidth(100, {
7982     duration: 1,
7983     callback: this.foo,
7984     scope: this
7985 });
7986
7987 // using the "anim" property to get the Anim object
7988 var opt = {
7989     duration: 1,
7990     callback: this.foo,
7991     scope: this
7992 };
7993 el.setWidth(100, opt);
7994 ...
7995 if(opt.anim.isAnimated()){
7996     opt.anim.stop();
7997 }
7998 </code></pre>
7999 * <b> Composite (Collections of) Elements</b><br />
8000  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8001  * @constructor Create a new Element directly.
8002  * @param {String/HTMLElement} element
8003  * @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).
8004  */
8005     Roo.Element = function(element, forceNew)
8006     {
8007         var dom = typeof element == "string" ?
8008                 document.getElementById(element) : element;
8009         
8010         this.listeners = {};
8011         
8012         if(!dom){ // invalid id/element
8013             return null;
8014         }
8015         var id = dom.id;
8016         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8017             return Roo.Element.cache[id];
8018         }
8019
8020         /**
8021          * The DOM element
8022          * @type HTMLElement
8023          */
8024         this.dom = dom;
8025
8026         /**
8027          * The DOM element ID
8028          * @type String
8029          */
8030         this.id = id || Roo.id(dom);
8031         
8032         return this; // assumed for cctor?
8033     };
8034
8035     var El = Roo.Element;
8036
8037     El.prototype = {
8038         /**
8039          * The element's default display mode  (defaults to "") 
8040          * @type String
8041          */
8042         originalDisplay : "",
8043
8044         
8045         // note this is overridden in BS version..
8046         visibilityMode : 1, 
8047         /**
8048          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8049          * @type String
8050          */
8051         defaultUnit : "px",
8052         
8053         /**
8054          * Sets the element's visibility mode. When setVisible() is called it
8055          * will use this to determine whether to set the visibility or the display property.
8056          * @param visMode Element.VISIBILITY or Element.DISPLAY
8057          * @return {Roo.Element} this
8058          */
8059         setVisibilityMode : function(visMode){
8060             this.visibilityMode = visMode;
8061             return this;
8062         },
8063         /**
8064          * Convenience method for setVisibilityMode(Element.DISPLAY)
8065          * @param {String} display (optional) What to set display to when visible
8066          * @return {Roo.Element} this
8067          */
8068         enableDisplayMode : function(display){
8069             this.setVisibilityMode(El.DISPLAY);
8070             if(typeof display != "undefined") { this.originalDisplay = display; }
8071             return this;
8072         },
8073
8074         /**
8075          * 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)
8076          * @param {String} selector The simple selector to test
8077          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8078                 search as a number or element (defaults to 10 || document.body)
8079          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8080          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8081          */
8082         findParent : function(simpleSelector, maxDepth, returnEl){
8083             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8084             maxDepth = maxDepth || 50;
8085             if(typeof maxDepth != "number"){
8086                 stopEl = Roo.getDom(maxDepth);
8087                 maxDepth = 10;
8088             }
8089             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8090                 if(dq.is(p, simpleSelector)){
8091                     return returnEl ? Roo.get(p) : p;
8092                 }
8093                 depth++;
8094                 p = p.parentNode;
8095             }
8096             return null;
8097         },
8098
8099
8100         /**
8101          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8102          * @param {String} selector The simple selector to test
8103          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8104                 search as a number or element (defaults to 10 || document.body)
8105          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8106          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8107          */
8108         findParentNode : function(simpleSelector, maxDepth, returnEl){
8109             var p = Roo.fly(this.dom.parentNode, '_internal');
8110             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8111         },
8112         
8113         /**
8114          * Looks at  the scrollable parent element
8115          */
8116         findScrollableParent : function()
8117         {
8118             var overflowRegex = /(auto|scroll)/;
8119             
8120             if(this.getStyle('position') === 'fixed'){
8121                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8122             }
8123             
8124             var excludeStaticParent = this.getStyle('position') === "absolute";
8125             
8126             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8127                 
8128                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8129                     continue;
8130                 }
8131                 
8132                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8133                     return parent;
8134                 }
8135                 
8136                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8137                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8138                 }
8139             }
8140             
8141             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8142         },
8143
8144         /**
8145          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8146          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8147          * @param {String} selector The simple selector to test
8148          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8149                 search as a number or element (defaults to 10 || document.body)
8150          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8151          */
8152         up : function(simpleSelector, maxDepth){
8153             return this.findParentNode(simpleSelector, maxDepth, true);
8154         },
8155
8156
8157
8158         /**
8159          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8160          * @param {String} selector The simple selector to test
8161          * @return {Boolean} True if this element matches the selector, else false
8162          */
8163         is : function(simpleSelector){
8164             return Roo.DomQuery.is(this.dom, simpleSelector);
8165         },
8166
8167         /**
8168          * Perform animation on this element.
8169          * @param {Object} args The YUI animation control args
8170          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8171          * @param {Function} onComplete (optional) Function to call when animation completes
8172          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8173          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8174          * @return {Roo.Element} this
8175          */
8176         animate : function(args, duration, onComplete, easing, animType){
8177             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8178             return this;
8179         },
8180
8181         /*
8182          * @private Internal animation call
8183          */
8184         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8185             animType = animType || 'run';
8186             opt = opt || {};
8187             var anim = Roo.lib.Anim[animType](
8188                 this.dom, args,
8189                 (opt.duration || defaultDur) || .35,
8190                 (opt.easing || defaultEase) || 'easeOut',
8191                 function(){
8192                     Roo.callback(cb, this);
8193                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8194                 },
8195                 this
8196             );
8197             opt.anim = anim;
8198             return anim;
8199         },
8200
8201         // private legacy anim prep
8202         preanim : function(a, i){
8203             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8204         },
8205
8206         /**
8207          * Removes worthless text nodes
8208          * @param {Boolean} forceReclean (optional) By default the element
8209          * keeps track if it has been cleaned already so
8210          * you can call this over and over. However, if you update the element and
8211          * need to force a reclean, you can pass true.
8212          */
8213         clean : function(forceReclean){
8214             if(this.isCleaned && forceReclean !== true){
8215                 return this;
8216             }
8217             var ns = /\S/;
8218             var d = this.dom, n = d.firstChild, ni = -1;
8219             while(n){
8220                 var nx = n.nextSibling;
8221                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8222                     d.removeChild(n);
8223                 }else{
8224                     n.nodeIndex = ++ni;
8225                 }
8226                 n = nx;
8227             }
8228             this.isCleaned = true;
8229             return this;
8230         },
8231
8232         // private
8233         calcOffsetsTo : function(el){
8234             el = Roo.get(el);
8235             var d = el.dom;
8236             var restorePos = false;
8237             if(el.getStyle('position') == 'static'){
8238                 el.position('relative');
8239                 restorePos = true;
8240             }
8241             var x = 0, y =0;
8242             var op = this.dom;
8243             while(op && op != d && op.tagName != 'HTML'){
8244                 x+= op.offsetLeft;
8245                 y+= op.offsetTop;
8246                 op = op.offsetParent;
8247             }
8248             if(restorePos){
8249                 el.position('static');
8250             }
8251             return [x, y];
8252         },
8253
8254         /**
8255          * Scrolls this element into view within the passed container.
8256          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8257          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8258          * @return {Roo.Element} this
8259          */
8260         scrollIntoView : function(container, hscroll){
8261             var c = Roo.getDom(container) || document.body;
8262             var el = this.dom;
8263
8264             var o = this.calcOffsetsTo(c),
8265                 l = o[0],
8266                 t = o[1],
8267                 b = t+el.offsetHeight,
8268                 r = l+el.offsetWidth;
8269
8270             var ch = c.clientHeight;
8271             var ct = parseInt(c.scrollTop, 10);
8272             var cl = parseInt(c.scrollLeft, 10);
8273             var cb = ct + ch;
8274             var cr = cl + c.clientWidth;
8275
8276             if(t < ct){
8277                 c.scrollTop = t;
8278             }else if(b > cb){
8279                 c.scrollTop = b-ch;
8280             }
8281
8282             if(hscroll !== false){
8283                 if(l < cl){
8284                     c.scrollLeft = l;
8285                 }else if(r > cr){
8286                     c.scrollLeft = r-c.clientWidth;
8287                 }
8288             }
8289             return this;
8290         },
8291
8292         // private
8293         scrollChildIntoView : function(child, hscroll){
8294             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8295         },
8296
8297         /**
8298          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8299          * the new height may not be available immediately.
8300          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8301          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8302          * @param {Function} onComplete (optional) Function to call when animation completes
8303          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8304          * @return {Roo.Element} this
8305          */
8306         autoHeight : function(animate, duration, onComplete, easing){
8307             var oldHeight = this.getHeight();
8308             this.clip();
8309             this.setHeight(1); // force clipping
8310             setTimeout(function(){
8311                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8312                 if(!animate){
8313                     this.setHeight(height);
8314                     this.unclip();
8315                     if(typeof onComplete == "function"){
8316                         onComplete();
8317                     }
8318                 }else{
8319                     this.setHeight(oldHeight); // restore original height
8320                     this.setHeight(height, animate, duration, function(){
8321                         this.unclip();
8322                         if(typeof onComplete == "function") { onComplete(); }
8323                     }.createDelegate(this), easing);
8324                 }
8325             }.createDelegate(this), 0);
8326             return this;
8327         },
8328
8329         /**
8330          * Returns true if this element is an ancestor of the passed element
8331          * @param {HTMLElement/String} el The element to check
8332          * @return {Boolean} True if this element is an ancestor of el, else false
8333          */
8334         contains : function(el){
8335             if(!el){return false;}
8336             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8337         },
8338
8339         /**
8340          * Checks whether the element is currently visible using both visibility and display properties.
8341          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8342          * @return {Boolean} True if the element is currently visible, else false
8343          */
8344         isVisible : function(deep) {
8345             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8346             if(deep !== true || !vis){
8347                 return vis;
8348             }
8349             var p = this.dom.parentNode;
8350             while(p && p.tagName.toLowerCase() != "body"){
8351                 if(!Roo.fly(p, '_isVisible').isVisible()){
8352                     return false;
8353                 }
8354                 p = p.parentNode;
8355             }
8356             return true;
8357         },
8358
8359         /**
8360          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8361          * @param {String} selector The CSS selector
8362          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8363          * @return {CompositeElement/CompositeElementLite} The composite element
8364          */
8365         select : function(selector, unique){
8366             return El.select(selector, unique, this.dom);
8367         },
8368
8369         /**
8370          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8371          * @param {String} selector The CSS selector
8372          * @return {Array} An array of the matched nodes
8373          */
8374         query : function(selector, unique){
8375             return Roo.DomQuery.select(selector, this.dom);
8376         },
8377
8378         /**
8379          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8380          * @param {String} selector The CSS selector
8381          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8382          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8383          */
8384         child : function(selector, returnDom){
8385             var n = Roo.DomQuery.selectNode(selector, this.dom);
8386             return returnDom ? n : Roo.get(n);
8387         },
8388
8389         /**
8390          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8391          * @param {String} selector The CSS selector
8392          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8393          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8394          */
8395         down : function(selector, returnDom){
8396             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8397             return returnDom ? n : Roo.get(n);
8398         },
8399
8400         /**
8401          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8402          * @param {String} group The group the DD object is member of
8403          * @param {Object} config The DD config object
8404          * @param {Object} overrides An object containing methods to override/implement on the DD object
8405          * @return {Roo.dd.DD} The DD object
8406          */
8407         initDD : function(group, config, overrides){
8408             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8409             return Roo.apply(dd, overrides);
8410         },
8411
8412         /**
8413          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8414          * @param {String} group The group the DDProxy object is member of
8415          * @param {Object} config The DDProxy config object
8416          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8417          * @return {Roo.dd.DDProxy} The DDProxy object
8418          */
8419         initDDProxy : function(group, config, overrides){
8420             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8421             return Roo.apply(dd, overrides);
8422         },
8423
8424         /**
8425          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8426          * @param {String} group The group the DDTarget object is member of
8427          * @param {Object} config The DDTarget config object
8428          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8429          * @return {Roo.dd.DDTarget} The DDTarget object
8430          */
8431         initDDTarget : function(group, config, overrides){
8432             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8433             return Roo.apply(dd, overrides);
8434         },
8435
8436         /**
8437          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8438          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8439          * @param {Boolean} visible Whether the element is visible
8440          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8441          * @return {Roo.Element} this
8442          */
8443          setVisible : function(visible, animate){
8444             if(!animate || !A){
8445                 if(this.visibilityMode == El.DISPLAY){
8446                     this.setDisplayed(visible);
8447                 }else{
8448                     this.fixDisplay();
8449                     this.dom.style.visibility = visible ? "visible" : "hidden";
8450                 }
8451             }else{
8452                 // closure for composites
8453                 var dom = this.dom;
8454                 var visMode = this.visibilityMode;
8455                 if(visible){
8456                     this.setOpacity(.01);
8457                     this.setVisible(true);
8458                 }
8459                 this.anim({opacity: { to: (visible?1:0) }},
8460                       this.preanim(arguments, 1),
8461                       null, .35, 'easeIn', function(){
8462                          if(!visible){
8463                              if(visMode == El.DISPLAY){
8464                                  dom.style.display = "none";
8465                              }else{
8466                                  dom.style.visibility = "hidden";
8467                              }
8468                              Roo.get(dom).setOpacity(1);
8469                          }
8470                      });
8471             }
8472             return this;
8473         },
8474
8475         /**
8476          * Returns true if display is not "none"
8477          * @return {Boolean}
8478          */
8479         isDisplayed : function() {
8480             return this.getStyle("display") != "none";
8481         },
8482
8483         /**
8484          * Toggles the element's visibility or display, depending on visibility mode.
8485          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8486          * @return {Roo.Element} this
8487          */
8488         toggle : function(animate){
8489             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8490             return this;
8491         },
8492
8493         /**
8494          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8495          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8496          * @return {Roo.Element} this
8497          */
8498         setDisplayed : function(value) {
8499             if(typeof value == "boolean"){
8500                value = value ? this.originalDisplay : "none";
8501             }
8502             this.setStyle("display", value);
8503             return this;
8504         },
8505
8506         /**
8507          * Tries to focus the element. Any exceptions are caught and ignored.
8508          * @return {Roo.Element} this
8509          */
8510         focus : function() {
8511             try{
8512                 this.dom.focus();
8513             }catch(e){}
8514             return this;
8515         },
8516
8517         /**
8518          * Tries to blur the element. Any exceptions are caught and ignored.
8519          * @return {Roo.Element} this
8520          */
8521         blur : function() {
8522             try{
8523                 this.dom.blur();
8524             }catch(e){}
8525             return this;
8526         },
8527
8528         /**
8529          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8530          * @param {String/Array} className The CSS class to add, or an array of classes
8531          * @return {Roo.Element} this
8532          */
8533         addClass : function(className){
8534             if(className instanceof Array){
8535                 for(var i = 0, len = className.length; i < len; i++) {
8536                     this.addClass(className[i]);
8537                 }
8538             }else{
8539                 if(className && !this.hasClass(className)){
8540                     if (this.dom instanceof SVGElement) {
8541                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8542                     } else {
8543                         this.dom.className = this.dom.className + " " + className;
8544                     }
8545                 }
8546             }
8547             return this;
8548         },
8549
8550         /**
8551          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8552          * @param {String/Array} className The CSS class to add, or an array of classes
8553          * @return {Roo.Element} this
8554          */
8555         radioClass : function(className){
8556             var siblings = this.dom.parentNode.childNodes;
8557             for(var i = 0; i < siblings.length; i++) {
8558                 var s = siblings[i];
8559                 if(s.nodeType == 1){
8560                     Roo.get(s).removeClass(className);
8561                 }
8562             }
8563             this.addClass(className);
8564             return this;
8565         },
8566
8567         /**
8568          * Removes one or more CSS classes from the element.
8569          * @param {String/Array} className The CSS class to remove, or an array of classes
8570          * @return {Roo.Element} this
8571          */
8572         removeClass : function(className){
8573             
8574             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8575             if(!className || !cn){
8576                 return this;
8577             }
8578             if(className instanceof Array){
8579                 for(var i = 0, len = className.length; i < len; i++) {
8580                     this.removeClass(className[i]);
8581                 }
8582             }else{
8583                 if(this.hasClass(className)){
8584                     var re = this.classReCache[className];
8585                     if (!re) {
8586                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8587                        this.classReCache[className] = re;
8588                     }
8589                     if (this.dom instanceof SVGElement) {
8590                         this.dom.className.baseVal = cn.replace(re, " ");
8591                     } else {
8592                         this.dom.className = cn.replace(re, " ");
8593                     }
8594                 }
8595             }
8596             return this;
8597         },
8598
8599         // private
8600         classReCache: {},
8601
8602         /**
8603          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8604          * @param {String} className The CSS class to toggle
8605          * @return {Roo.Element} this
8606          */
8607         toggleClass : function(className){
8608             if(this.hasClass(className)){
8609                 this.removeClass(className);
8610             }else{
8611                 this.addClass(className);
8612             }
8613             return this;
8614         },
8615
8616         /**
8617          * Checks if the specified CSS class exists on this element's DOM node.
8618          * @param {String} className The CSS class to check for
8619          * @return {Boolean} True if the class exists, else false
8620          */
8621         hasClass : function(className){
8622             if (this.dom instanceof SVGElement) {
8623                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8624             } 
8625             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8626         },
8627
8628         /**
8629          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8630          * @param {String} oldClassName The CSS class to replace
8631          * @param {String} newClassName The replacement CSS class
8632          * @return {Roo.Element} this
8633          */
8634         replaceClass : function(oldClassName, newClassName){
8635             this.removeClass(oldClassName);
8636             this.addClass(newClassName);
8637             return this;
8638         },
8639
8640         /**
8641          * Returns an object with properties matching the styles requested.
8642          * For example, el.getStyles('color', 'font-size', 'width') might return
8643          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8644          * @param {String} style1 A style name
8645          * @param {String} style2 A style name
8646          * @param {String} etc.
8647          * @return {Object} The style object
8648          */
8649         getStyles : function(){
8650             var a = arguments, len = a.length, r = {};
8651             for(var i = 0; i < len; i++){
8652                 r[a[i]] = this.getStyle(a[i]);
8653             }
8654             return r;
8655         },
8656
8657         /**
8658          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8659          * @param {String} property The style property whose value is returned.
8660          * @return {String} The current value of the style property for this element.
8661          */
8662         getStyle : function(){
8663             return view && view.getComputedStyle ?
8664                 function(prop){
8665                     var el = this.dom, v, cs, camel;
8666                     if(prop == 'float'){
8667                         prop = "cssFloat";
8668                     }
8669                     if(el.style && (v = el.style[prop])){
8670                         return v;
8671                     }
8672                     if(cs = view.getComputedStyle(el, "")){
8673                         if(!(camel = propCache[prop])){
8674                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8675                         }
8676                         return cs[camel];
8677                     }
8678                     return null;
8679                 } :
8680                 function(prop){
8681                     var el = this.dom, v, cs, camel;
8682                     if(prop == 'opacity'){
8683                         if(typeof el.style.filter == 'string'){
8684                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8685                             if(m){
8686                                 var fv = parseFloat(m[1]);
8687                                 if(!isNaN(fv)){
8688                                     return fv ? fv / 100 : 0;
8689                                 }
8690                             }
8691                         }
8692                         return 1;
8693                     }else if(prop == 'float'){
8694                         prop = "styleFloat";
8695                     }
8696                     if(!(camel = propCache[prop])){
8697                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8698                     }
8699                     if(v = el.style[camel]){
8700                         return v;
8701                     }
8702                     if(cs = el.currentStyle){
8703                         return cs[camel];
8704                     }
8705                     return null;
8706                 };
8707         }(),
8708
8709         /**
8710          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8711          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8712          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8713          * @return {Roo.Element} this
8714          */
8715         setStyle : function(prop, value){
8716             if(typeof prop == "string"){
8717                 
8718                 if (prop == 'float') {
8719                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8720                     return this;
8721                 }
8722                 
8723                 var camel;
8724                 if(!(camel = propCache[prop])){
8725                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8726                 }
8727                 
8728                 if(camel == 'opacity') {
8729                     this.setOpacity(value);
8730                 }else{
8731                     this.dom.style[camel] = value;
8732                 }
8733             }else{
8734                 for(var style in prop){
8735                     if(typeof prop[style] != "function"){
8736                        this.setStyle(style, prop[style]);
8737                     }
8738                 }
8739             }
8740             return this;
8741         },
8742
8743         /**
8744          * More flexible version of {@link #setStyle} for setting style properties.
8745          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8746          * a function which returns such a specification.
8747          * @return {Roo.Element} this
8748          */
8749         applyStyles : function(style){
8750             Roo.DomHelper.applyStyles(this.dom, style);
8751             return this;
8752         },
8753
8754         /**
8755           * 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).
8756           * @return {Number} The X position of the element
8757           */
8758         getX : function(){
8759             return D.getX(this.dom);
8760         },
8761
8762         /**
8763           * 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).
8764           * @return {Number} The Y position of the element
8765           */
8766         getY : function(){
8767             return D.getY(this.dom);
8768         },
8769
8770         /**
8771           * 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).
8772           * @return {Array} The XY position of the element
8773           */
8774         getXY : function(){
8775             return D.getXY(this.dom);
8776         },
8777
8778         /**
8779          * 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).
8780          * @param {Number} The X position of the element
8781          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8782          * @return {Roo.Element} this
8783          */
8784         setX : function(x, animate){
8785             if(!animate || !A){
8786                 D.setX(this.dom, x);
8787             }else{
8788                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8789             }
8790             return this;
8791         },
8792
8793         /**
8794          * 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).
8795          * @param {Number} The Y position of the element
8796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8797          * @return {Roo.Element} this
8798          */
8799         setY : function(y, animate){
8800             if(!animate || !A){
8801                 D.setY(this.dom, y);
8802             }else{
8803                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8804             }
8805             return this;
8806         },
8807
8808         /**
8809          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8810          * @param {String} left The left CSS property value
8811          * @return {Roo.Element} this
8812          */
8813         setLeft : function(left){
8814             this.setStyle("left", this.addUnits(left));
8815             return this;
8816         },
8817
8818         /**
8819          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8820          * @param {String} top The top CSS property value
8821          * @return {Roo.Element} this
8822          */
8823         setTop : function(top){
8824             this.setStyle("top", this.addUnits(top));
8825             return this;
8826         },
8827
8828         /**
8829          * Sets the element's CSS right style.
8830          * @param {String} right The right CSS property value
8831          * @return {Roo.Element} this
8832          */
8833         setRight : function(right){
8834             this.setStyle("right", this.addUnits(right));
8835             return this;
8836         },
8837
8838         /**
8839          * Sets the element's CSS bottom style.
8840          * @param {String} bottom The bottom CSS property value
8841          * @return {Roo.Element} this
8842          */
8843         setBottom : function(bottom){
8844             this.setStyle("bottom", this.addUnits(bottom));
8845             return this;
8846         },
8847
8848         /**
8849          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8850          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8851          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8852          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8853          * @return {Roo.Element} this
8854          */
8855         setXY : function(pos, animate){
8856             if(!animate || !A){
8857                 D.setXY(this.dom, pos);
8858             }else{
8859                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8860             }
8861             return this;
8862         },
8863
8864         /**
8865          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8866          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8867          * @param {Number} x X value for new position (coordinates are page-based)
8868          * @param {Number} y Y value for new position (coordinates are page-based)
8869          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8870          * @return {Roo.Element} this
8871          */
8872         setLocation : function(x, y, animate){
8873             this.setXY([x, y], this.preanim(arguments, 2));
8874             return this;
8875         },
8876
8877         /**
8878          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8879          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8880          * @param {Number} x X value for new position (coordinates are page-based)
8881          * @param {Number} y Y value for new position (coordinates are page-based)
8882          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8883          * @return {Roo.Element} this
8884          */
8885         moveTo : function(x, y, animate){
8886             this.setXY([x, y], this.preanim(arguments, 2));
8887             return this;
8888         },
8889
8890         /**
8891          * Returns the region of the given element.
8892          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8893          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8894          */
8895         getRegion : function(){
8896             return D.getRegion(this.dom);
8897         },
8898
8899         /**
8900          * Returns the offset height of the element
8901          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8902          * @return {Number} The element's height
8903          */
8904         getHeight : function(contentHeight){
8905             var h = this.dom.offsetHeight || 0;
8906             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8907         },
8908
8909         /**
8910          * Returns the offset width of the element
8911          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8912          * @return {Number} The element's width
8913          */
8914         getWidth : function(contentWidth){
8915             var w = this.dom.offsetWidth || 0;
8916             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8917         },
8918
8919         /**
8920          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8921          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8922          * if a height has not been set using CSS.
8923          * @return {Number}
8924          */
8925         getComputedHeight : function(){
8926             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8927             if(!h){
8928                 h = parseInt(this.getStyle('height'), 10) || 0;
8929                 if(!this.isBorderBox()){
8930                     h += this.getFrameWidth('tb');
8931                 }
8932             }
8933             return h;
8934         },
8935
8936         /**
8937          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8938          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8939          * if a width has not been set using CSS.
8940          * @return {Number}
8941          */
8942         getComputedWidth : function(){
8943             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8944             if(!w){
8945                 w = parseInt(this.getStyle('width'), 10) || 0;
8946                 if(!this.isBorderBox()){
8947                     w += this.getFrameWidth('lr');
8948                 }
8949             }
8950             return w;
8951         },
8952
8953         /**
8954          * Returns the size of the element.
8955          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8956          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8957          */
8958         getSize : function(contentSize){
8959             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8960         },
8961
8962         /**
8963          * Returns the width and height of the viewport.
8964          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8965          */
8966         getViewSize : function(){
8967             var d = this.dom, doc = document, aw = 0, ah = 0;
8968             if(d == doc || d == doc.body){
8969                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8970             }else{
8971                 return {
8972                     width : d.clientWidth,
8973                     height: d.clientHeight
8974                 };
8975             }
8976         },
8977
8978         /**
8979          * Returns the value of the "value" attribute
8980          * @param {Boolean} asNumber true to parse the value as a number
8981          * @return {String/Number}
8982          */
8983         getValue : function(asNumber){
8984             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8985         },
8986
8987         // private
8988         adjustWidth : function(width){
8989             if(typeof width == "number"){
8990                 if(this.autoBoxAdjust && !this.isBorderBox()){
8991                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8992                 }
8993                 if(width < 0){
8994                     width = 0;
8995                 }
8996             }
8997             return width;
8998         },
8999
9000         // private
9001         adjustHeight : function(height){
9002             if(typeof height == "number"){
9003                if(this.autoBoxAdjust && !this.isBorderBox()){
9004                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9005                }
9006                if(height < 0){
9007                    height = 0;
9008                }
9009             }
9010             return height;
9011         },
9012
9013         /**
9014          * Set the width of the element
9015          * @param {Number} width The new width
9016          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9017          * @return {Roo.Element} this
9018          */
9019         setWidth : function(width, animate){
9020             width = this.adjustWidth(width);
9021             if(!animate || !A){
9022                 this.dom.style.width = this.addUnits(width);
9023             }else{
9024                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9025             }
9026             return this;
9027         },
9028
9029         /**
9030          * Set the height of the element
9031          * @param {Number} height The new height
9032          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9033          * @return {Roo.Element} this
9034          */
9035          setHeight : function(height, animate){
9036             height = this.adjustHeight(height);
9037             if(!animate || !A){
9038                 this.dom.style.height = this.addUnits(height);
9039             }else{
9040                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9041             }
9042             return this;
9043         },
9044
9045         /**
9046          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9047          * @param {Number} width The new width
9048          * @param {Number} height The new height
9049          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9050          * @return {Roo.Element} this
9051          */
9052          setSize : function(width, height, animate){
9053             if(typeof width == "object"){ // in case of object from getSize()
9054                 height = width.height; width = width.width;
9055             }
9056             width = this.adjustWidth(width); height = this.adjustHeight(height);
9057             if(!animate || !A){
9058                 this.dom.style.width = this.addUnits(width);
9059                 this.dom.style.height = this.addUnits(height);
9060             }else{
9061                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9062             }
9063             return this;
9064         },
9065
9066         /**
9067          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9068          * @param {Number} x X value for new position (coordinates are page-based)
9069          * @param {Number} y Y value for new position (coordinates are page-based)
9070          * @param {Number} width The new width
9071          * @param {Number} height The new height
9072          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9073          * @return {Roo.Element} this
9074          */
9075         setBounds : function(x, y, width, height, animate){
9076             if(!animate || !A){
9077                 this.setSize(width, height);
9078                 this.setLocation(x, y);
9079             }else{
9080                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9081                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9082                               this.preanim(arguments, 4), 'motion');
9083             }
9084             return this;
9085         },
9086
9087         /**
9088          * 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.
9089          * @param {Roo.lib.Region} region The region to fill
9090          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9091          * @return {Roo.Element} this
9092          */
9093         setRegion : function(region, animate){
9094             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9095             return this;
9096         },
9097
9098         /**
9099          * Appends an event handler
9100          *
9101          * @param {String}   eventName     The type of event to append
9102          * @param {Function} fn        The method the event invokes
9103          * @param {Object} scope       (optional) The scope (this object) of the fn
9104          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9105          */
9106         addListener : function(eventName, fn, scope, options)
9107         {
9108             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9109                 this.addListener('touchstart', this.onTapHandler, this);
9110             }
9111             
9112             // we need to handle a special case where dom element is a svg element.
9113             // in this case we do not actua
9114             if (!this.dom) {
9115                 return;
9116             }
9117             
9118             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9119                 if (typeof(this.listeners[eventName]) == 'undefined') {
9120                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9121                 }
9122                 this.listeners[eventName].addListener(fn, scope, options);
9123                 return;
9124             }
9125             
9126                 
9127             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9128             
9129             
9130         },
9131         tapedTwice : false,
9132         onTapHandler : function(event)
9133         {
9134             if(!this.tapedTwice) {
9135                 this.tapedTwice = true;
9136                 var s = this;
9137                 setTimeout( function() {
9138                     s.tapedTwice = false;
9139                 }, 300 );
9140                 return;
9141             }
9142             event.preventDefault();
9143             var revent = new MouseEvent('dblclick',  {
9144                 view: window,
9145                 bubbles: true,
9146                 cancelable: true
9147             });
9148              
9149             this.dom.dispatchEvent(revent);
9150             //action on double tap goes below
9151              
9152         }, 
9153  
9154         /**
9155          * Removes an event handler from this element
9156          * @param {String} eventName the type of event to remove
9157          * @param {Function} fn the method the event invokes
9158          * @param {Function} scope (needed for svg fake listeners)
9159          * @return {Roo.Element} this
9160          */
9161         removeListener : function(eventName, fn, scope){
9162             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9163             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9164                 return this;
9165             }
9166             this.listeners[eventName].removeListener(fn, scope);
9167             return this;
9168         },
9169
9170         /**
9171          * Removes all previous added listeners from this element
9172          * @return {Roo.Element} this
9173          */
9174         removeAllListeners : function(){
9175             E.purgeElement(this.dom);
9176             this.listeners = {};
9177             return this;
9178         },
9179
9180         relayEvent : function(eventName, observable){
9181             this.on(eventName, function(e){
9182                 observable.fireEvent(eventName, e);
9183             });
9184         },
9185
9186         
9187         /**
9188          * Set the opacity of the element
9189          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9190          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9191          * @return {Roo.Element} this
9192          */
9193          setOpacity : function(opacity, animate){
9194             if(!animate || !A){
9195                 var s = this.dom.style;
9196                 if(Roo.isIE){
9197                     s.zoom = 1;
9198                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9199                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9200                 }else{
9201                     s.opacity = opacity;
9202                 }
9203             }else{
9204                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9205             }
9206             return this;
9207         },
9208
9209         /**
9210          * Gets the left X coordinate
9211          * @param {Boolean} local True to get the local css position instead of page coordinate
9212          * @return {Number}
9213          */
9214         getLeft : function(local){
9215             if(!local){
9216                 return this.getX();
9217             }else{
9218                 return parseInt(this.getStyle("left"), 10) || 0;
9219             }
9220         },
9221
9222         /**
9223          * Gets the right X coordinate of the element (element X position + element width)
9224          * @param {Boolean} local True to get the local css position instead of page coordinate
9225          * @return {Number}
9226          */
9227         getRight : function(local){
9228             if(!local){
9229                 return this.getX() + this.getWidth();
9230             }else{
9231                 return (this.getLeft(true) + this.getWidth()) || 0;
9232             }
9233         },
9234
9235         /**
9236          * Gets the top Y coordinate
9237          * @param {Boolean} local True to get the local css position instead of page coordinate
9238          * @return {Number}
9239          */
9240         getTop : function(local) {
9241             if(!local){
9242                 return this.getY();
9243             }else{
9244                 return parseInt(this.getStyle("top"), 10) || 0;
9245             }
9246         },
9247
9248         /**
9249          * Gets the bottom Y coordinate of the element (element Y position + element height)
9250          * @param {Boolean} local True to get the local css position instead of page coordinate
9251          * @return {Number}
9252          */
9253         getBottom : function(local){
9254             if(!local){
9255                 return this.getY() + this.getHeight();
9256             }else{
9257                 return (this.getTop(true) + this.getHeight()) || 0;
9258             }
9259         },
9260
9261         /**
9262         * Initializes positioning on this element. If a desired position is not passed, it will make the
9263         * the element positioned relative IF it is not already positioned.
9264         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9265         * @param {Number} zIndex (optional) The zIndex to apply
9266         * @param {Number} x (optional) Set the page X position
9267         * @param {Number} y (optional) Set the page Y position
9268         */
9269         position : function(pos, zIndex, x, y){
9270             if(!pos){
9271                if(this.getStyle('position') == 'static'){
9272                    this.setStyle('position', 'relative');
9273                }
9274             }else{
9275                 this.setStyle("position", pos);
9276             }
9277             if(zIndex){
9278                 this.setStyle("z-index", zIndex);
9279             }
9280             if(x !== undefined && y !== undefined){
9281                 this.setXY([x, y]);
9282             }else if(x !== undefined){
9283                 this.setX(x);
9284             }else if(y !== undefined){
9285                 this.setY(y);
9286             }
9287         },
9288
9289         /**
9290         * Clear positioning back to the default when the document was loaded
9291         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9292         * @return {Roo.Element} this
9293          */
9294         clearPositioning : function(value){
9295             value = value ||'';
9296             this.setStyle({
9297                 "left": value,
9298                 "right": value,
9299                 "top": value,
9300                 "bottom": value,
9301                 "z-index": "",
9302                 "position" : "static"
9303             });
9304             return this;
9305         },
9306
9307         /**
9308         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9309         * snapshot before performing an update and then restoring the element.
9310         * @return {Object}
9311         */
9312         getPositioning : function(){
9313             var l = this.getStyle("left");
9314             var t = this.getStyle("top");
9315             return {
9316                 "position" : this.getStyle("position"),
9317                 "left" : l,
9318                 "right" : l ? "" : this.getStyle("right"),
9319                 "top" : t,
9320                 "bottom" : t ? "" : this.getStyle("bottom"),
9321                 "z-index" : this.getStyle("z-index")
9322             };
9323         },
9324
9325         /**
9326          * Gets the width of the border(s) for the specified side(s)
9327          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9328          * passing lr would get the border (l)eft width + the border (r)ight width.
9329          * @return {Number} The width of the sides passed added together
9330          */
9331         getBorderWidth : function(side){
9332             return this.addStyles(side, El.borders);
9333         },
9334
9335         /**
9336          * Gets the width of the padding(s) for the specified side(s)
9337          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9338          * passing lr would get the padding (l)eft + the padding (r)ight.
9339          * @return {Number} The padding of the sides passed added together
9340          */
9341         getPadding : function(side){
9342             return this.addStyles(side, El.paddings);
9343         },
9344
9345         /**
9346         * Set positioning with an object returned by getPositioning().
9347         * @param {Object} posCfg
9348         * @return {Roo.Element} this
9349          */
9350         setPositioning : function(pc){
9351             this.applyStyles(pc);
9352             if(pc.right == "auto"){
9353                 this.dom.style.right = "";
9354             }
9355             if(pc.bottom == "auto"){
9356                 this.dom.style.bottom = "";
9357             }
9358             return this;
9359         },
9360
9361         // private
9362         fixDisplay : function(){
9363             if(this.getStyle("display") == "none"){
9364                 this.setStyle("visibility", "hidden");
9365                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9366                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9367                     this.setStyle("display", "block");
9368                 }
9369             }
9370         },
9371
9372         /**
9373          * Quick set left and top adding default units
9374          * @param {String} left The left CSS property value
9375          * @param {String} top The top CSS property value
9376          * @return {Roo.Element} this
9377          */
9378          setLeftTop : function(left, top){
9379             this.dom.style.left = this.addUnits(left);
9380             this.dom.style.top = this.addUnits(top);
9381             return this;
9382         },
9383
9384         /**
9385          * Move this element relative to its current position.
9386          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9387          * @param {Number} distance How far to move the element in pixels
9388          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9389          * @return {Roo.Element} this
9390          */
9391          move : function(direction, distance, animate){
9392             var xy = this.getXY();
9393             direction = direction.toLowerCase();
9394             switch(direction){
9395                 case "l":
9396                 case "left":
9397                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9398                     break;
9399                case "r":
9400                case "right":
9401                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9402                     break;
9403                case "t":
9404                case "top":
9405                case "up":
9406                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9407                     break;
9408                case "b":
9409                case "bottom":
9410                case "down":
9411                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9412                     break;
9413             }
9414             return this;
9415         },
9416
9417         /**
9418          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9419          * @return {Roo.Element} this
9420          */
9421         clip : function(){
9422             if(!this.isClipped){
9423                this.isClipped = true;
9424                this.originalClip = {
9425                    "o": this.getStyle("overflow"),
9426                    "x": this.getStyle("overflow-x"),
9427                    "y": this.getStyle("overflow-y")
9428                };
9429                this.setStyle("overflow", "hidden");
9430                this.setStyle("overflow-x", "hidden");
9431                this.setStyle("overflow-y", "hidden");
9432             }
9433             return this;
9434         },
9435
9436         /**
9437          *  Return clipping (overflow) to original clipping before clip() was called
9438          * @return {Roo.Element} this
9439          */
9440         unclip : function(){
9441             if(this.isClipped){
9442                 this.isClipped = false;
9443                 var o = this.originalClip;
9444                 if(o.o){this.setStyle("overflow", o.o);}
9445                 if(o.x){this.setStyle("overflow-x", o.x);}
9446                 if(o.y){this.setStyle("overflow-y", o.y);}
9447             }
9448             return this;
9449         },
9450
9451
9452         /**
9453          * Gets the x,y coordinates specified by the anchor position on the element.
9454          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9455          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9456          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9457          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9458          * @return {Array} [x, y] An array containing the element's x and y coordinates
9459          */
9460         getAnchorXY : function(anchor, local, s){
9461             //Passing a different size is useful for pre-calculating anchors,
9462             //especially for anchored animations that change the el size.
9463
9464             var w, h, vp = false;
9465             if(!s){
9466                 var d = this.dom;
9467                 if(d == document.body || d == document){
9468                     vp = true;
9469                     w = D.getViewWidth(); h = D.getViewHeight();
9470                 }else{
9471                     w = this.getWidth(); h = this.getHeight();
9472                 }
9473             }else{
9474                 w = s.width;  h = s.height;
9475             }
9476             var x = 0, y = 0, r = Math.round;
9477             switch((anchor || "tl").toLowerCase()){
9478                 case "c":
9479                     x = r(w*.5);
9480                     y = r(h*.5);
9481                 break;
9482                 case "t":
9483                     x = r(w*.5);
9484                     y = 0;
9485                 break;
9486                 case "l":
9487                     x = 0;
9488                     y = r(h*.5);
9489                 break;
9490                 case "r":
9491                     x = w;
9492                     y = r(h*.5);
9493                 break;
9494                 case "b":
9495                     x = r(w*.5);
9496                     y = h;
9497                 break;
9498                 case "tl":
9499                     x = 0;
9500                     y = 0;
9501                 break;
9502                 case "bl":
9503                     x = 0;
9504                     y = h;
9505                 break;
9506                 case "br":
9507                     x = w;
9508                     y = h;
9509                 break;
9510                 case "tr":
9511                     x = w;
9512                     y = 0;
9513                 break;
9514             }
9515             if(local === true){
9516                 return [x, y];
9517             }
9518             if(vp){
9519                 var sc = this.getScroll();
9520                 return [x + sc.left, y + sc.top];
9521             }
9522             //Add the element's offset xy
9523             var o = this.getXY();
9524             return [x+o[0], y+o[1]];
9525         },
9526
9527         /**
9528          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9529          * supported position values.
9530          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9531          * @param {String} position The position to align to.
9532          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9533          * @return {Array} [x, y]
9534          */
9535         getAlignToXY : function(el, p, o)
9536         {
9537             el = Roo.get(el);
9538             var d = this.dom;
9539             if(!el.dom){
9540                 throw "Element.alignTo with an element that doesn't exist";
9541             }
9542             var c = false; //constrain to viewport
9543             var p1 = "", p2 = "";
9544             o = o || [0,0];
9545
9546             if(!p){
9547                 p = "tl-bl";
9548             }else if(p == "?"){
9549                 p = "tl-bl?";
9550             }else if(p.indexOf("-") == -1){
9551                 p = "tl-" + p;
9552             }
9553             p = p.toLowerCase();
9554             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9555             if(!m){
9556                throw "Element.alignTo with an invalid alignment " + p;
9557             }
9558             p1 = m[1]; p2 = m[2]; c = !!m[3];
9559
9560             //Subtract the aligned el's internal xy from the target's offset xy
9561             //plus custom offset to get the aligned el's new offset xy
9562             var a1 = this.getAnchorXY(p1, true);
9563             var a2 = el.getAnchorXY(p2, false);
9564             var x = a2[0] - a1[0] + o[0];
9565             var y = a2[1] - a1[1] + o[1];
9566             if(c){
9567                 //constrain the aligned el to viewport if necessary
9568                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9569                 // 5px of margin for ie
9570                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9571
9572                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9573                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9574                 //otherwise swap the aligned el to the opposite border of the target.
9575                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9576                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9577                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9578                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9579
9580                var doc = document;
9581                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9582                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9583
9584                if((x+w) > dw + scrollX){
9585                     x = swapX ? r.left-w : dw+scrollX-w;
9586                 }
9587                if(x < scrollX){
9588                    x = swapX ? r.right : scrollX;
9589                }
9590                if((y+h) > dh + scrollY){
9591                     y = swapY ? r.top-h : dh+scrollY-h;
9592                 }
9593                if (y < scrollY){
9594                    y = swapY ? r.bottom : scrollY;
9595                }
9596             }
9597             return [x,y];
9598         },
9599
9600         // private
9601         getConstrainToXY : function(){
9602             var os = {top:0, left:0, bottom:0, right: 0};
9603
9604             return function(el, local, offsets, proposedXY){
9605                 el = Roo.get(el);
9606                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9607
9608                 var vw, vh, vx = 0, vy = 0;
9609                 if(el.dom == document.body || el.dom == document){
9610                     vw = Roo.lib.Dom.getViewWidth();
9611                     vh = Roo.lib.Dom.getViewHeight();
9612                 }else{
9613                     vw = el.dom.clientWidth;
9614                     vh = el.dom.clientHeight;
9615                     if(!local){
9616                         var vxy = el.getXY();
9617                         vx = vxy[0];
9618                         vy = vxy[1];
9619                     }
9620                 }
9621
9622                 var s = el.getScroll();
9623
9624                 vx += offsets.left + s.left;
9625                 vy += offsets.top + s.top;
9626
9627                 vw -= offsets.right;
9628                 vh -= offsets.bottom;
9629
9630                 var vr = vx+vw;
9631                 var vb = vy+vh;
9632
9633                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9634                 var x = xy[0], y = xy[1];
9635                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9636
9637                 // only move it if it needs it
9638                 var moved = false;
9639
9640                 // first validate right/bottom
9641                 if((x + w) > vr){
9642                     x = vr - w;
9643                     moved = true;
9644                 }
9645                 if((y + h) > vb){
9646                     y = vb - h;
9647                     moved = true;
9648                 }
9649                 // then make sure top/left isn't negative
9650                 if(x < vx){
9651                     x = vx;
9652                     moved = true;
9653                 }
9654                 if(y < vy){
9655                     y = vy;
9656                     moved = true;
9657                 }
9658                 return moved ? [x, y] : false;
9659             };
9660         }(),
9661
9662         // private
9663         adjustForConstraints : function(xy, parent, offsets){
9664             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9665         },
9666
9667         /**
9668          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9669          * document it aligns it to the viewport.
9670          * The position parameter is optional, and can be specified in any one of the following formats:
9671          * <ul>
9672          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9673          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9674          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9675          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9676          *   <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
9677          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9678          * </ul>
9679          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9680          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9681          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9682          * that specified in order to enforce the viewport constraints.
9683          * Following are all of the supported anchor positions:
9684     <pre>
9685     Value  Description
9686     -----  -----------------------------
9687     tl     The top left corner (default)
9688     t      The center of the top edge
9689     tr     The top right corner
9690     l      The center of the left edge
9691     c      In the center of the element
9692     r      The center of the right edge
9693     bl     The bottom left corner
9694     b      The center of the bottom edge
9695     br     The bottom right corner
9696     </pre>
9697     Example Usage:
9698     <pre><code>
9699     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9700     el.alignTo("other-el");
9701
9702     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9703     el.alignTo("other-el", "tr?");
9704
9705     // align the bottom right corner of el with the center left edge of other-el
9706     el.alignTo("other-el", "br-l?");
9707
9708     // align the center of el with the bottom left corner of other-el and
9709     // adjust the x position by -6 pixels (and the y position by 0)
9710     el.alignTo("other-el", "c-bl", [-6, 0]);
9711     </code></pre>
9712          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9713          * @param {String} position The position to align to.
9714          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9715          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9716          * @return {Roo.Element} this
9717          */
9718         alignTo : function(element, position, offsets, animate){
9719             var xy = this.getAlignToXY(element, position, offsets);
9720             this.setXY(xy, this.preanim(arguments, 3));
9721             return this;
9722         },
9723
9724         /**
9725          * Anchors an element to another element and realigns it when the window is resized.
9726          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9727          * @param {String} position The position to align to.
9728          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9729          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9730          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9731          * is a number, it is used as the buffer delay (defaults to 50ms).
9732          * @param {Function} callback The function to call after the animation finishes
9733          * @return {Roo.Element} this
9734          */
9735         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9736             var action = function(){
9737                 this.alignTo(el, alignment, offsets, animate);
9738                 Roo.callback(callback, this);
9739             };
9740             Roo.EventManager.onWindowResize(action, this);
9741             var tm = typeof monitorScroll;
9742             if(tm != 'undefined'){
9743                 Roo.EventManager.on(window, 'scroll', action, this,
9744                     {buffer: tm == 'number' ? monitorScroll : 50});
9745             }
9746             action.call(this); // align immediately
9747             return this;
9748         },
9749         /**
9750          * Clears any opacity settings from this element. Required in some cases for IE.
9751          * @return {Roo.Element} this
9752          */
9753         clearOpacity : function(){
9754             if (window.ActiveXObject) {
9755                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9756                     this.dom.style.filter = "";
9757                 }
9758             } else {
9759                 this.dom.style.opacity = "";
9760                 this.dom.style["-moz-opacity"] = "";
9761                 this.dom.style["-khtml-opacity"] = "";
9762             }
9763             return this;
9764         },
9765
9766         /**
9767          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9768          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9769          * @return {Roo.Element} this
9770          */
9771         hide : function(animate){
9772             this.setVisible(false, this.preanim(arguments, 0));
9773             return this;
9774         },
9775
9776         /**
9777         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9778         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9779          * @return {Roo.Element} this
9780          */
9781         show : function(animate){
9782             this.setVisible(true, this.preanim(arguments, 0));
9783             return this;
9784         },
9785
9786         /**
9787          * @private Test if size has a unit, otherwise appends the default
9788          */
9789         addUnits : function(size){
9790             return Roo.Element.addUnits(size, this.defaultUnit);
9791         },
9792
9793         /**
9794          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9795          * @return {Roo.Element} this
9796          */
9797         beginMeasure : function(){
9798             var el = this.dom;
9799             if(el.offsetWidth || el.offsetHeight){
9800                 return this; // offsets work already
9801             }
9802             var changed = [];
9803             var p = this.dom, b = document.body; // start with this element
9804             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9805                 var pe = Roo.get(p);
9806                 if(pe.getStyle('display') == 'none'){
9807                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9808                     p.style.visibility = "hidden";
9809                     p.style.display = "block";
9810                 }
9811                 p = p.parentNode;
9812             }
9813             this._measureChanged = changed;
9814             return this;
9815
9816         },
9817
9818         /**
9819          * Restores displays to before beginMeasure was called
9820          * @return {Roo.Element} this
9821          */
9822         endMeasure : function(){
9823             var changed = this._measureChanged;
9824             if(changed){
9825                 for(var i = 0, len = changed.length; i < len; i++) {
9826                     var r = changed[i];
9827                     r.el.style.visibility = r.visibility;
9828                     r.el.style.display = "none";
9829                 }
9830                 this._measureChanged = null;
9831             }
9832             return this;
9833         },
9834
9835         /**
9836         * Update the innerHTML of this element, optionally searching for and processing scripts
9837         * @param {String} html The new HTML
9838         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9839         * @param {Function} callback For async script loading you can be noticed when the update completes
9840         * @return {Roo.Element} this
9841          */
9842         update : function(html, loadScripts, callback){
9843             if(typeof html == "undefined"){
9844                 html = "";
9845             }
9846             if(loadScripts !== true){
9847                 this.dom.innerHTML = html;
9848                 if(typeof callback == "function"){
9849                     callback();
9850                 }
9851                 return this;
9852             }
9853             var id = Roo.id();
9854             var dom = this.dom;
9855
9856             html += '<span id="' + id + '"></span>';
9857
9858             E.onAvailable(id, function(){
9859                 var hd = document.getElementsByTagName("head")[0];
9860                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9861                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9862                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9863
9864                 var match;
9865                 while(match = re.exec(html)){
9866                     var attrs = match[1];
9867                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9868                     if(srcMatch && srcMatch[2]){
9869                        var s = document.createElement("script");
9870                        s.src = srcMatch[2];
9871                        var typeMatch = attrs.match(typeRe);
9872                        if(typeMatch && typeMatch[2]){
9873                            s.type = typeMatch[2];
9874                        }
9875                        hd.appendChild(s);
9876                     }else if(match[2] && match[2].length > 0){
9877                         if(window.execScript) {
9878                            window.execScript(match[2]);
9879                         } else {
9880                             /**
9881                              * eval:var:id
9882                              * eval:var:dom
9883                              * eval:var:html
9884                              * 
9885                              */
9886                            window.eval(match[2]);
9887                         }
9888                     }
9889                 }
9890                 var el = document.getElementById(id);
9891                 if(el){el.parentNode.removeChild(el);}
9892                 if(typeof callback == "function"){
9893                     callback();
9894                 }
9895             });
9896             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9897             return this;
9898         },
9899
9900         /**
9901          * Direct access to the UpdateManager update() method (takes the same parameters).
9902          * @param {String/Function} url The url for this request or a function to call to get the url
9903          * @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}
9904          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9905          * @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.
9906          * @return {Roo.Element} this
9907          */
9908         load : function(){
9909             var um = this.getUpdateManager();
9910             um.update.apply(um, arguments);
9911             return this;
9912         },
9913
9914         /**
9915         * Gets this element's UpdateManager
9916         * @return {Roo.UpdateManager} The UpdateManager
9917         */
9918         getUpdateManager : function(){
9919             if(!this.updateManager){
9920                 this.updateManager = new Roo.UpdateManager(this);
9921             }
9922             return this.updateManager;
9923         },
9924
9925         /**
9926          * Disables text selection for this element (normalized across browsers)
9927          * @return {Roo.Element} this
9928          */
9929         unselectable : function(){
9930             this.dom.unselectable = "on";
9931             this.swallowEvent("selectstart", true);
9932             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9933             this.addClass("x-unselectable");
9934             return this;
9935         },
9936
9937         /**
9938         * Calculates the x, y to center this element on the screen
9939         * @return {Array} The x, y values [x, y]
9940         */
9941         getCenterXY : function(){
9942             return this.getAlignToXY(document, 'c-c');
9943         },
9944
9945         /**
9946         * Centers the Element in either the viewport, or another Element.
9947         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9948         */
9949         center : function(centerIn){
9950             this.alignTo(centerIn || document, 'c-c');
9951             return this;
9952         },
9953
9954         /**
9955          * Tests various css rules/browsers to determine if this element uses a border box
9956          * @return {Boolean}
9957          */
9958         isBorderBox : function(){
9959             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9960         },
9961
9962         /**
9963          * Return a box {x, y, width, height} that can be used to set another elements
9964          * size/location to match this element.
9965          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9966          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9967          * @return {Object} box An object in the format {x, y, width, height}
9968          */
9969         getBox : function(contentBox, local){
9970             var xy;
9971             if(!local){
9972                 xy = this.getXY();
9973             }else{
9974                 var left = parseInt(this.getStyle("left"), 10) || 0;
9975                 var top = parseInt(this.getStyle("top"), 10) || 0;
9976                 xy = [left, top];
9977             }
9978             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9979             if(!contentBox){
9980                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9981             }else{
9982                 var l = this.getBorderWidth("l")+this.getPadding("l");
9983                 var r = this.getBorderWidth("r")+this.getPadding("r");
9984                 var t = this.getBorderWidth("t")+this.getPadding("t");
9985                 var b = this.getBorderWidth("b")+this.getPadding("b");
9986                 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)};
9987             }
9988             bx.right = bx.x + bx.width;
9989             bx.bottom = bx.y + bx.height;
9990             return bx;
9991         },
9992
9993         /**
9994          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9995          for more information about the sides.
9996          * @param {String} sides
9997          * @return {Number}
9998          */
9999         getFrameWidth : function(sides, onlyContentBox){
10000             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10001         },
10002
10003         /**
10004          * 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.
10005          * @param {Object} box The box to fill {x, y, width, height}
10006          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10007          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10008          * @return {Roo.Element} this
10009          */
10010         setBox : function(box, adjust, animate){
10011             var w = box.width, h = box.height;
10012             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10013                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10014                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10015             }
10016             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10017             return this;
10018         },
10019
10020         /**
10021          * Forces the browser to repaint this element
10022          * @return {Roo.Element} this
10023          */
10024          repaint : function(){
10025             var dom = this.dom;
10026             this.addClass("x-repaint");
10027             setTimeout(function(){
10028                 Roo.get(dom).removeClass("x-repaint");
10029             }, 1);
10030             return this;
10031         },
10032
10033         /**
10034          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10035          * then it returns the calculated width of the sides (see getPadding)
10036          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10037          * @return {Object/Number}
10038          */
10039         getMargins : function(side){
10040             if(!side){
10041                 return {
10042                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10043                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10044                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10045                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10046                 };
10047             }else{
10048                 return this.addStyles(side, El.margins);
10049              }
10050         },
10051
10052         // private
10053         addStyles : function(sides, styles){
10054             var val = 0, v, w;
10055             for(var i = 0, len = sides.length; i < len; i++){
10056                 v = this.getStyle(styles[sides.charAt(i)]);
10057                 if(v){
10058                      w = parseInt(v, 10);
10059                      if(w){ val += w; }
10060                 }
10061             }
10062             return val;
10063         },
10064
10065         /**
10066          * Creates a proxy element of this element
10067          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10068          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10069          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10070          * @return {Roo.Element} The new proxy element
10071          */
10072         createProxy : function(config, renderTo, matchBox){
10073             if(renderTo){
10074                 renderTo = Roo.getDom(renderTo);
10075             }else{
10076                 renderTo = document.body;
10077             }
10078             config = typeof config == "object" ?
10079                 config : {tag : "div", cls: config};
10080             var proxy = Roo.DomHelper.append(renderTo, config, true);
10081             if(matchBox){
10082                proxy.setBox(this.getBox());
10083             }
10084             return proxy;
10085         },
10086
10087         /**
10088          * Puts a mask over this element to disable user interaction. Requires core.css.
10089          * This method can only be applied to elements which accept child nodes.
10090          * @param {String} msg (optional) A message to display in the mask
10091          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10092          * @return {Element} The mask  element
10093          */
10094         mask : function(msg, msgCls)
10095         {
10096             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10097                 this.setStyle("position", "relative");
10098             }
10099             if(!this._mask){
10100                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10101             }
10102             
10103             this.addClass("x-masked");
10104             this._mask.setDisplayed(true);
10105             
10106             // we wander
10107             var z = 0;
10108             var dom = this.dom;
10109             while (dom && dom.style) {
10110                 if (!isNaN(parseInt(dom.style.zIndex))) {
10111                     z = Math.max(z, parseInt(dom.style.zIndex));
10112                 }
10113                 dom = dom.parentNode;
10114             }
10115             // if we are masking the body - then it hides everything..
10116             if (this.dom == document.body) {
10117                 z = 1000000;
10118                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10119                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10120             }
10121            
10122             if(typeof msg == 'string'){
10123                 if(!this._maskMsg){
10124                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10125                         cls: "roo-el-mask-msg", 
10126                         cn: [
10127                             {
10128                                 tag: 'i',
10129                                 cls: 'fa fa-spinner fa-spin'
10130                             },
10131                             {
10132                                 tag: 'div'
10133                             }   
10134                         ]
10135                     }, true);
10136                 }
10137                 var mm = this._maskMsg;
10138                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10139                 if (mm.dom.lastChild) { // weird IE issue?
10140                     mm.dom.lastChild.innerHTML = msg;
10141                 }
10142                 mm.setDisplayed(true);
10143                 mm.center(this);
10144                 mm.setStyle('z-index', z + 102);
10145             }
10146             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10147                 this._mask.setHeight(this.getHeight());
10148             }
10149             this._mask.setStyle('z-index', z + 100);
10150             
10151             return this._mask;
10152         },
10153
10154         /**
10155          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10156          * it is cached for reuse.
10157          */
10158         unmask : function(removeEl){
10159             if(this._mask){
10160                 if(removeEl === true){
10161                     this._mask.remove();
10162                     delete this._mask;
10163                     if(this._maskMsg){
10164                         this._maskMsg.remove();
10165                         delete this._maskMsg;
10166                     }
10167                 }else{
10168                     this._mask.setDisplayed(false);
10169                     if(this._maskMsg){
10170                         this._maskMsg.setDisplayed(false);
10171                     }
10172                 }
10173             }
10174             this.removeClass("x-masked");
10175         },
10176
10177         /**
10178          * Returns true if this element is masked
10179          * @return {Boolean}
10180          */
10181         isMasked : function(){
10182             return this._mask && this._mask.isVisible();
10183         },
10184
10185         /**
10186          * Creates an iframe shim for this element to keep selects and other windowed objects from
10187          * showing through.
10188          * @return {Roo.Element} The new shim element
10189          */
10190         createShim : function(){
10191             var el = document.createElement('iframe');
10192             el.frameBorder = 'no';
10193             el.className = 'roo-shim';
10194             if(Roo.isIE && Roo.isSecure){
10195                 el.src = Roo.SSL_SECURE_URL;
10196             }
10197             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10198             shim.autoBoxAdjust = false;
10199             return shim;
10200         },
10201
10202         /**
10203          * Removes this element from the DOM and deletes it from the cache
10204          */
10205         remove : function(){
10206             if(this.dom.parentNode){
10207                 this.dom.parentNode.removeChild(this.dom);
10208             }
10209             delete El.cache[this.dom.id];
10210         },
10211
10212         /**
10213          * Sets up event handlers to add and remove a css class when the mouse is over this element
10214          * @param {String} className
10215          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10216          * mouseout events for children elements
10217          * @return {Roo.Element} this
10218          */
10219         addClassOnOver : function(className, preventFlicker){
10220             this.on("mouseover", function(){
10221                 Roo.fly(this, '_internal').addClass(className);
10222             }, this.dom);
10223             var removeFn = function(e){
10224                 if(preventFlicker !== true || !e.within(this, true)){
10225                     Roo.fly(this, '_internal').removeClass(className);
10226                 }
10227             };
10228             this.on("mouseout", removeFn, this.dom);
10229             return this;
10230         },
10231
10232         /**
10233          * Sets up event handlers to add and remove a css class when this element has the focus
10234          * @param {String} className
10235          * @return {Roo.Element} this
10236          */
10237         addClassOnFocus : function(className){
10238             this.on("focus", function(){
10239                 Roo.fly(this, '_internal').addClass(className);
10240             }, this.dom);
10241             this.on("blur", function(){
10242                 Roo.fly(this, '_internal').removeClass(className);
10243             }, this.dom);
10244             return this;
10245         },
10246         /**
10247          * 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)
10248          * @param {String} className
10249          * @return {Roo.Element} this
10250          */
10251         addClassOnClick : function(className){
10252             var dom = this.dom;
10253             this.on("mousedown", function(){
10254                 Roo.fly(dom, '_internal').addClass(className);
10255                 var d = Roo.get(document);
10256                 var fn = function(){
10257                     Roo.fly(dom, '_internal').removeClass(className);
10258                     d.removeListener("mouseup", fn);
10259                 };
10260                 d.on("mouseup", fn);
10261             });
10262             return this;
10263         },
10264
10265         /**
10266          * Stops the specified event from bubbling and optionally prevents the default action
10267          * @param {String} eventName
10268          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10269          * @return {Roo.Element} this
10270          */
10271         swallowEvent : function(eventName, preventDefault){
10272             var fn = function(e){
10273                 e.stopPropagation();
10274                 if(preventDefault){
10275                     e.preventDefault();
10276                 }
10277             };
10278             if(eventName instanceof Array){
10279                 for(var i = 0, len = eventName.length; i < len; i++){
10280                      this.on(eventName[i], fn);
10281                 }
10282                 return this;
10283             }
10284             this.on(eventName, fn);
10285             return this;
10286         },
10287
10288         /**
10289          * @private
10290          */
10291         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10292
10293         /**
10294          * Sizes this element to its parent element's dimensions performing
10295          * neccessary box adjustments.
10296          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10297          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10298          * @return {Roo.Element} this
10299          */
10300         fitToParent : function(monitorResize, targetParent) {
10301           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10302           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10303           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10304             return this;
10305           }
10306           var p = Roo.get(targetParent || this.dom.parentNode);
10307           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10308           if (monitorResize === true) {
10309             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10310             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10311           }
10312           return this;
10313         },
10314
10315         /**
10316          * Gets the next sibling, skipping text nodes
10317          * @return {HTMLElement} The next sibling or null
10318          */
10319         getNextSibling : function(){
10320             var n = this.dom.nextSibling;
10321             while(n && n.nodeType != 1){
10322                 n = n.nextSibling;
10323             }
10324             return n;
10325         },
10326
10327         /**
10328          * Gets the previous sibling, skipping text nodes
10329          * @return {HTMLElement} The previous sibling or null
10330          */
10331         getPrevSibling : function(){
10332             var n = this.dom.previousSibling;
10333             while(n && n.nodeType != 1){
10334                 n = n.previousSibling;
10335             }
10336             return n;
10337         },
10338
10339
10340         /**
10341          * Appends the passed element(s) to this element
10342          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10343          * @return {Roo.Element} this
10344          */
10345         appendChild: function(el){
10346             el = Roo.get(el);
10347             el.appendTo(this);
10348             return this;
10349         },
10350
10351         /**
10352          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10353          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10354          * automatically generated with the specified attributes.
10355          * @param {HTMLElement} insertBefore (optional) a child element of this element
10356          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10357          * @return {Roo.Element} The new child element
10358          */
10359         createChild: function(config, insertBefore, returnDom){
10360             config = config || {tag:'div'};
10361             if(insertBefore){
10362                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10363             }
10364             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10365         },
10366
10367         /**
10368          * Appends this element to the passed element
10369          * @param {String/HTMLElement/Element} el The new parent element
10370          * @return {Roo.Element} this
10371          */
10372         appendTo: function(el){
10373             el = Roo.getDom(el);
10374             el.appendChild(this.dom);
10375             return this;
10376         },
10377
10378         /**
10379          * Inserts this element before the passed element in the DOM
10380          * @param {String/HTMLElement/Element} el The element to insert before
10381          * @return {Roo.Element} this
10382          */
10383         insertBefore: function(el){
10384             el = Roo.getDom(el);
10385             el.parentNode.insertBefore(this.dom, el);
10386             return this;
10387         },
10388
10389         /**
10390          * Inserts this element after the passed element in the DOM
10391          * @param {String/HTMLElement/Element} el The element to insert after
10392          * @return {Roo.Element} this
10393          */
10394         insertAfter: function(el){
10395             el = Roo.getDom(el);
10396             el.parentNode.insertBefore(this.dom, el.nextSibling);
10397             return this;
10398         },
10399
10400         /**
10401          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10402          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10403          * @return {Roo.Element} The new child
10404          */
10405         insertFirst: function(el, returnDom){
10406             el = el || {};
10407             if(typeof el == 'object' && !el.nodeType){ // dh config
10408                 return this.createChild(el, this.dom.firstChild, returnDom);
10409             }else{
10410                 el = Roo.getDom(el);
10411                 this.dom.insertBefore(el, this.dom.firstChild);
10412                 return !returnDom ? Roo.get(el) : el;
10413             }
10414         },
10415
10416         /**
10417          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10418          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10419          * @param {String} where (optional) 'before' or 'after' defaults to before
10420          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10421          * @return {Roo.Element} the inserted Element
10422          */
10423         insertSibling: function(el, where, returnDom){
10424             where = where ? where.toLowerCase() : 'before';
10425             el = el || {};
10426             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10427
10428             if(typeof el == 'object' && !el.nodeType){ // dh config
10429                 if(where == 'after' && !this.dom.nextSibling){
10430                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10431                 }else{
10432                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10433                 }
10434
10435             }else{
10436                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10437                             where == 'before' ? this.dom : this.dom.nextSibling);
10438                 if(!returnDom){
10439                     rt = Roo.get(rt);
10440                 }
10441             }
10442             return rt;
10443         },
10444
10445         /**
10446          * Creates and wraps this element with another element
10447          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10448          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10449          * @return {HTMLElement/Element} The newly created wrapper element
10450          */
10451         wrap: function(config, returnDom){
10452             if(!config){
10453                 config = {tag: "div"};
10454             }
10455             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10456             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10457             return newEl;
10458         },
10459
10460         /**
10461          * Replaces the passed element with this element
10462          * @param {String/HTMLElement/Element} el The element to replace
10463          * @return {Roo.Element} this
10464          */
10465         replace: function(el){
10466             el = Roo.get(el);
10467             this.insertBefore(el);
10468             el.remove();
10469             return this;
10470         },
10471
10472         /**
10473          * Inserts an html fragment into this element
10474          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10475          * @param {String} html The HTML fragment
10476          * @param {Boolean} returnEl True to return an Roo.Element
10477          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10478          */
10479         insertHtml : function(where, html, returnEl){
10480             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10481             return returnEl ? Roo.get(el) : el;
10482         },
10483
10484         /**
10485          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10486          * @param {Object} o The object with the attributes
10487          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10488          * @return {Roo.Element} this
10489          */
10490         set : function(o, useSet){
10491             var el = this.dom;
10492             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10493             for(var attr in o){
10494                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10495                 if(attr=="cls"){
10496                     el.className = o["cls"];
10497                 }else{
10498                     if(useSet) {
10499                         el.setAttribute(attr, o[attr]);
10500                     } else {
10501                         el[attr] = o[attr];
10502                     }
10503                 }
10504             }
10505             if(o.style){
10506                 Roo.DomHelper.applyStyles(el, o.style);
10507             }
10508             return this;
10509         },
10510
10511         /**
10512          * Convenience method for constructing a KeyMap
10513          * @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:
10514          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10515          * @param {Function} fn The function to call
10516          * @param {Object} scope (optional) The scope of the function
10517          * @return {Roo.KeyMap} The KeyMap created
10518          */
10519         addKeyListener : function(key, fn, scope){
10520             var config;
10521             if(typeof key != "object" || key instanceof Array){
10522                 config = {
10523                     key: key,
10524                     fn: fn,
10525                     scope: scope
10526                 };
10527             }else{
10528                 config = {
10529                     key : key.key,
10530                     shift : key.shift,
10531                     ctrl : key.ctrl,
10532                     alt : key.alt,
10533                     fn: fn,
10534                     scope: scope
10535                 };
10536             }
10537             return new Roo.KeyMap(this, config);
10538         },
10539
10540         /**
10541          * Creates a KeyMap for this element
10542          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10543          * @return {Roo.KeyMap} The KeyMap created
10544          */
10545         addKeyMap : function(config){
10546             return new Roo.KeyMap(this, config);
10547         },
10548
10549         /**
10550          * Returns true if this element is scrollable.
10551          * @return {Boolean}
10552          */
10553          isScrollable : function(){
10554             var dom = this.dom;
10555             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10556         },
10557
10558         /**
10559          * 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().
10560          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10561          * @param {Number} value The new scroll value
10562          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10563          * @return {Element} this
10564          */
10565
10566         scrollTo : function(side, value, animate){
10567             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10568             if(!animate || !A){
10569                 this.dom[prop] = value;
10570             }else{
10571                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10572                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10573             }
10574             return this;
10575         },
10576
10577         /**
10578          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10579          * within this element's scrollable range.
10580          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10581          * @param {Number} distance How far to scroll the element in pixels
10582          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10583          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10584          * was scrolled as far as it could go.
10585          */
10586          scroll : function(direction, distance, animate){
10587              if(!this.isScrollable()){
10588                  return;
10589              }
10590              var el = this.dom;
10591              var l = el.scrollLeft, t = el.scrollTop;
10592              var w = el.scrollWidth, h = el.scrollHeight;
10593              var cw = el.clientWidth, ch = el.clientHeight;
10594              direction = direction.toLowerCase();
10595              var scrolled = false;
10596              var a = this.preanim(arguments, 2);
10597              switch(direction){
10598                  case "l":
10599                  case "left":
10600                      if(w - l > cw){
10601                          var v = Math.min(l + distance, w-cw);
10602                          this.scrollTo("left", v, a);
10603                          scrolled = true;
10604                      }
10605                      break;
10606                 case "r":
10607                 case "right":
10608                      if(l > 0){
10609                          var v = Math.max(l - distance, 0);
10610                          this.scrollTo("left", v, a);
10611                          scrolled = true;
10612                      }
10613                      break;
10614                 case "t":
10615                 case "top":
10616                 case "up":
10617                      if(t > 0){
10618                          var v = Math.max(t - distance, 0);
10619                          this.scrollTo("top", v, a);
10620                          scrolled = true;
10621                      }
10622                      break;
10623                 case "b":
10624                 case "bottom":
10625                 case "down":
10626                      if(h - t > ch){
10627                          var v = Math.min(t + distance, h-ch);
10628                          this.scrollTo("top", v, a);
10629                          scrolled = true;
10630                      }
10631                      break;
10632              }
10633              return scrolled;
10634         },
10635
10636         /**
10637          * Translates the passed page coordinates into left/top css values for this element
10638          * @param {Number/Array} x The page x or an array containing [x, y]
10639          * @param {Number} y The page y
10640          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10641          */
10642         translatePoints : function(x, y){
10643             if(typeof x == 'object' || x instanceof Array){
10644                 y = x[1]; x = x[0];
10645             }
10646             var p = this.getStyle('position');
10647             var o = this.getXY();
10648
10649             var l = parseInt(this.getStyle('left'), 10);
10650             var t = parseInt(this.getStyle('top'), 10);
10651
10652             if(isNaN(l)){
10653                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10654             }
10655             if(isNaN(t)){
10656                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10657             }
10658
10659             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10660         },
10661
10662         /**
10663          * Returns the current scroll position of the element.
10664          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10665          */
10666         getScroll : function(){
10667             var d = this.dom, doc = document;
10668             if(d == doc || d == doc.body){
10669                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10670                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10671                 return {left: l, top: t};
10672             }else{
10673                 return {left: d.scrollLeft, top: d.scrollTop};
10674             }
10675         },
10676
10677         /**
10678          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10679          * are convert to standard 6 digit hex color.
10680          * @param {String} attr The css attribute
10681          * @param {String} defaultValue The default value to use when a valid color isn't found
10682          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10683          * YUI color anims.
10684          */
10685         getColor : function(attr, defaultValue, prefix){
10686             var v = this.getStyle(attr);
10687             if(!v || v == "transparent" || v == "inherit") {
10688                 return defaultValue;
10689             }
10690             var color = typeof prefix == "undefined" ? "#" : prefix;
10691             if(v.substr(0, 4) == "rgb("){
10692                 var rvs = v.slice(4, v.length -1).split(",");
10693                 for(var i = 0; i < 3; i++){
10694                     var h = parseInt(rvs[i]).toString(16);
10695                     if(h < 16){
10696                         h = "0" + h;
10697                     }
10698                     color += h;
10699                 }
10700             } else {
10701                 if(v.substr(0, 1) == "#"){
10702                     if(v.length == 4) {
10703                         for(var i = 1; i < 4; i++){
10704                             var c = v.charAt(i);
10705                             color +=  c + c;
10706                         }
10707                     }else if(v.length == 7){
10708                         color += v.substr(1);
10709                     }
10710                 }
10711             }
10712             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10713         },
10714
10715         /**
10716          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10717          * gradient background, rounded corners and a 4-way shadow.
10718          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10719          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10720          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10721          * @return {Roo.Element} this
10722          */
10723         boxWrap : function(cls){
10724             cls = cls || 'x-box';
10725             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10726             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10727             return el;
10728         },
10729
10730         /**
10731          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10732          * @param {String} namespace The namespace in which to look for the attribute
10733          * @param {String} name The attribute name
10734          * @return {String} The attribute value
10735          */
10736         getAttributeNS : Roo.isIE ? function(ns, name){
10737             var d = this.dom;
10738             var type = typeof d[ns+":"+name];
10739             if(type != 'undefined' && type != 'unknown'){
10740                 return d[ns+":"+name];
10741             }
10742             return d[name];
10743         } : function(ns, name){
10744             var d = this.dom;
10745             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10746         },
10747         
10748         
10749         /**
10750          * Sets or Returns the value the dom attribute value
10751          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10752          * @param {String} value (optional) The value to set the attribute to
10753          * @return {String} The attribute value
10754          */
10755         attr : function(name){
10756             if (arguments.length > 1) {
10757                 this.dom.setAttribute(name, arguments[1]);
10758                 return arguments[1];
10759             }
10760             if (typeof(name) == 'object') {
10761                 for(var i in name) {
10762                     this.attr(i, name[i]);
10763                 }
10764                 return name;
10765             }
10766             
10767             
10768             if (!this.dom.hasAttribute(name)) {
10769                 return undefined;
10770             }
10771             return this.dom.getAttribute(name);
10772         }
10773         
10774         
10775         
10776     };
10777
10778     var ep = El.prototype;
10779
10780     /**
10781      * Appends an event handler (Shorthand for addListener)
10782      * @param {String}   eventName     The type of event to append
10783      * @param {Function} fn        The method the event invokes
10784      * @param {Object} scope       (optional) The scope (this object) of the fn
10785      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10786      * @method
10787      */
10788     ep.on = ep.addListener;
10789         // backwards compat
10790     ep.mon = ep.addListener;
10791
10792     /**
10793      * Removes an event handler from this element (shorthand for removeListener)
10794      * @param {String} eventName the type of event to remove
10795      * @param {Function} fn the method the event invokes
10796      * @return {Roo.Element} this
10797      * @method
10798      */
10799     ep.un = ep.removeListener;
10800
10801     /**
10802      * true to automatically adjust width and height settings for box-model issues (default to true)
10803      */
10804     ep.autoBoxAdjust = true;
10805
10806     // private
10807     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10808
10809     // private
10810     El.addUnits = function(v, defaultUnit){
10811         if(v === "" || v == "auto"){
10812             return v;
10813         }
10814         if(v === undefined){
10815             return '';
10816         }
10817         if(typeof v == "number" || !El.unitPattern.test(v)){
10818             return v + (defaultUnit || 'px');
10819         }
10820         return v;
10821     };
10822
10823     // special markup used throughout Roo when box wrapping elements
10824     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>';
10825     /**
10826      * Visibility mode constant - Use visibility to hide element
10827      * @static
10828      * @type Number
10829      */
10830     El.VISIBILITY = 1;
10831     /**
10832      * Visibility mode constant - Use display to hide element
10833      * @static
10834      * @type Number
10835      */
10836     El.DISPLAY = 2;
10837
10838     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10839     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10840     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10841
10842
10843
10844     /**
10845      * @private
10846      */
10847     El.cache = {};
10848
10849     var docEl;
10850
10851     /**
10852      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10853      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10854      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10855      * @return {Element} The Element object
10856      * @static
10857      */
10858     El.get = function(el){
10859         var ex, elm, id;
10860         if(!el){ return null; }
10861         if(typeof el == "string"){ // element id
10862             if(!(elm = document.getElementById(el))){
10863                 return null;
10864             }
10865             if(ex = El.cache[el]){
10866                 ex.dom = elm;
10867             }else{
10868                 ex = El.cache[el] = new El(elm);
10869             }
10870             return ex;
10871         }else if(el.tagName){ // dom element
10872             if(!(id = el.id)){
10873                 id = Roo.id(el);
10874             }
10875             if(ex = El.cache[id]){
10876                 ex.dom = el;
10877             }else{
10878                 ex = El.cache[id] = new El(el);
10879             }
10880             return ex;
10881         }else if(el instanceof El){
10882             if(el != docEl){
10883                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10884                                                               // catch case where it hasn't been appended
10885                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10886             }
10887             return el;
10888         }else if(el.isComposite){
10889             return el;
10890         }else if(el instanceof Array){
10891             return El.select(el);
10892         }else if(el == document){
10893             // create a bogus element object representing the document object
10894             if(!docEl){
10895                 var f = function(){};
10896                 f.prototype = El.prototype;
10897                 docEl = new f();
10898                 docEl.dom = document;
10899             }
10900             return docEl;
10901         }
10902         return null;
10903     };
10904
10905     // private
10906     El.uncache = function(el){
10907         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10908             if(a[i]){
10909                 delete El.cache[a[i].id || a[i]];
10910             }
10911         }
10912     };
10913
10914     // private
10915     // Garbage collection - uncache elements/purge listeners on orphaned elements
10916     // so we don't hold a reference and cause the browser to retain them
10917     El.garbageCollect = function(){
10918         if(!Roo.enableGarbageCollector){
10919             clearInterval(El.collectorThread);
10920             return;
10921         }
10922         for(var eid in El.cache){
10923             var el = El.cache[eid], d = el.dom;
10924             // -------------------------------------------------------
10925             // Determining what is garbage:
10926             // -------------------------------------------------------
10927             // !d
10928             // dom node is null, definitely garbage
10929             // -------------------------------------------------------
10930             // !d.parentNode
10931             // no parentNode == direct orphan, definitely garbage
10932             // -------------------------------------------------------
10933             // !d.offsetParent && !document.getElementById(eid)
10934             // display none elements have no offsetParent so we will
10935             // also try to look it up by it's id. However, check
10936             // offsetParent first so we don't do unneeded lookups.
10937             // This enables collection of elements that are not orphans
10938             // directly, but somewhere up the line they have an orphan
10939             // parent.
10940             // -------------------------------------------------------
10941             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10942                 delete El.cache[eid];
10943                 if(d && Roo.enableListenerCollection){
10944                     E.purgeElement(d);
10945                 }
10946             }
10947         }
10948     }
10949     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10950
10951
10952     // dom is optional
10953     El.Flyweight = function(dom){
10954         this.dom = dom;
10955     };
10956     El.Flyweight.prototype = El.prototype;
10957
10958     El._flyweights = {};
10959     /**
10960      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10961      * the dom node can be overwritten by other code.
10962      * @param {String/HTMLElement} el The dom node or id
10963      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10964      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10965      * @static
10966      * @return {Element} The shared Element object
10967      */
10968     El.fly = function(el, named){
10969         named = named || '_global';
10970         el = Roo.getDom(el);
10971         if(!el){
10972             return null;
10973         }
10974         if(!El._flyweights[named]){
10975             El._flyweights[named] = new El.Flyweight();
10976         }
10977         El._flyweights[named].dom = el;
10978         return El._flyweights[named];
10979     };
10980
10981     /**
10982      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10983      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10984      * Shorthand of {@link Roo.Element#get}
10985      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10986      * @return {Element} The Element object
10987      * @member Roo
10988      * @method get
10989      */
10990     Roo.get = El.get;
10991     /**
10992      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10993      * the dom node can be overwritten by other code.
10994      * Shorthand of {@link Roo.Element#fly}
10995      * @param {String/HTMLElement} el The dom node or id
10996      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10997      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10998      * @static
10999      * @return {Element} The shared Element object
11000      * @member Roo
11001      * @method fly
11002      */
11003     Roo.fly = El.fly;
11004
11005     // speedy lookup for elements never to box adjust
11006     var noBoxAdjust = Roo.isStrict ? {
11007         select:1
11008     } : {
11009         input:1, select:1, textarea:1
11010     };
11011     if(Roo.isIE || Roo.isGecko){
11012         noBoxAdjust['button'] = 1;
11013     }
11014
11015
11016     Roo.EventManager.on(window, 'unload', function(){
11017         delete El.cache;
11018         delete El._flyweights;
11019     });
11020 })();
11021
11022
11023
11024
11025 if(Roo.DomQuery){
11026     Roo.Element.selectorFunction = Roo.DomQuery.select;
11027 }
11028
11029 Roo.Element.select = function(selector, unique, root){
11030     var els;
11031     if(typeof selector == "string"){
11032         els = Roo.Element.selectorFunction(selector, root);
11033     }else if(selector.length !== undefined){
11034         els = selector;
11035     }else{
11036         throw "Invalid selector";
11037     }
11038     if(unique === true){
11039         return new Roo.CompositeElement(els);
11040     }else{
11041         return new Roo.CompositeElementLite(els);
11042     }
11043 };
11044 /**
11045  * Selects elements based on the passed CSS selector to enable working on them as 1.
11046  * @param {String/Array} selector The CSS selector or an array of elements
11047  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11048  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11049  * @return {CompositeElementLite/CompositeElement}
11050  * @member Roo
11051  * @method select
11052  */
11053 Roo.select = Roo.Element.select;
11054
11055
11056
11057
11058
11059
11060
11061
11062
11063
11064
11065
11066
11067
11068 /*
11069  * Based on:
11070  * Ext JS Library 1.1.1
11071  * Copyright(c) 2006-2007, Ext JS, LLC.
11072  *
11073  * Originally Released Under LGPL - original licence link has changed is not relivant.
11074  *
11075  * Fork - LGPL
11076  * <script type="text/javascript">
11077  */
11078
11079
11080
11081 //Notifies Element that fx methods are available
11082 Roo.enableFx = true;
11083
11084 /**
11085  * @class Roo.Fx
11086  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11087  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11088  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11089  * Element effects to work.</p><br/>
11090  *
11091  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11092  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11093  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11094  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11095  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11096  * expected results and should be done with care.</p><br/>
11097  *
11098  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11099  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11100 <pre>
11101 Value  Description
11102 -----  -----------------------------
11103 tl     The top left corner
11104 t      The center of the top edge
11105 tr     The top right corner
11106 l      The center of the left edge
11107 r      The center of the right edge
11108 bl     The bottom left corner
11109 b      The center of the bottom edge
11110 br     The bottom right corner
11111 </pre>
11112  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11113  * below are common options that can be passed to any Fx method.</b>
11114  * @cfg {Function} callback A function called when the effect is finished
11115  * @cfg {Object} scope The scope of the effect function
11116  * @cfg {String} easing A valid Easing value for the effect
11117  * @cfg {String} afterCls A css class to apply after the effect
11118  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11119  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11120  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11121  * effects that end with the element being visually hidden, ignored otherwise)
11122  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11123  * a function which returns such a specification that will be applied to the Element after the effect finishes
11124  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11125  * @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
11126  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11127  */
11128 Roo.Fx = {
11129         /**
11130          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11131          * origin for the slide effect.  This function automatically handles wrapping the element with
11132          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11133          * Usage:
11134          *<pre><code>
11135 // default: slide the element in from the top
11136 el.slideIn();
11137
11138 // custom: slide the element in from the right with a 2-second duration
11139 el.slideIn('r', { duration: 2 });
11140
11141 // common config options shown with default values
11142 el.slideIn('t', {
11143     easing: 'easeOut',
11144     duration: .5
11145 });
11146 </code></pre>
11147          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11148          * @param {Object} options (optional) Object literal with any of the Fx config options
11149          * @return {Roo.Element} The Element
11150          */
11151     slideIn : function(anchor, o){
11152         var el = this.getFxEl();
11153         o = o || {};
11154
11155         el.queueFx(o, function(){
11156
11157             anchor = anchor || "t";
11158
11159             // fix display to visibility
11160             this.fixDisplay();
11161
11162             // restore values after effect
11163             var r = this.getFxRestore();
11164             var b = this.getBox();
11165             // fixed size for slide
11166             this.setSize(b);
11167
11168             // wrap if needed
11169             var wrap = this.fxWrap(r.pos, o, "hidden");
11170
11171             var st = this.dom.style;
11172             st.visibility = "visible";
11173             st.position = "absolute";
11174
11175             // clear out temp styles after slide and unwrap
11176             var after = function(){
11177                 el.fxUnwrap(wrap, r.pos, o);
11178                 st.width = r.width;
11179                 st.height = r.height;
11180                 el.afterFx(o);
11181             };
11182             // time to calc the positions
11183             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11184
11185             switch(anchor.toLowerCase()){
11186                 case "t":
11187                     wrap.setSize(b.width, 0);
11188                     st.left = st.bottom = "0";
11189                     a = {height: bh};
11190                 break;
11191                 case "l":
11192                     wrap.setSize(0, b.height);
11193                     st.right = st.top = "0";
11194                     a = {width: bw};
11195                 break;
11196                 case "r":
11197                     wrap.setSize(0, b.height);
11198                     wrap.setX(b.right);
11199                     st.left = st.top = "0";
11200                     a = {width: bw, points: pt};
11201                 break;
11202                 case "b":
11203                     wrap.setSize(b.width, 0);
11204                     wrap.setY(b.bottom);
11205                     st.left = st.top = "0";
11206                     a = {height: bh, points: pt};
11207                 break;
11208                 case "tl":
11209                     wrap.setSize(0, 0);
11210                     st.right = st.bottom = "0";
11211                     a = {width: bw, height: bh};
11212                 break;
11213                 case "bl":
11214                     wrap.setSize(0, 0);
11215                     wrap.setY(b.y+b.height);
11216                     st.right = st.top = "0";
11217                     a = {width: bw, height: bh, points: pt};
11218                 break;
11219                 case "br":
11220                     wrap.setSize(0, 0);
11221                     wrap.setXY([b.right, b.bottom]);
11222                     st.left = st.top = "0";
11223                     a = {width: bw, height: bh, points: pt};
11224                 break;
11225                 case "tr":
11226                     wrap.setSize(0, 0);
11227                     wrap.setX(b.x+b.width);
11228                     st.left = st.bottom = "0";
11229                     a = {width: bw, height: bh, points: pt};
11230                 break;
11231             }
11232             this.dom.style.visibility = "visible";
11233             wrap.show();
11234
11235             arguments.callee.anim = wrap.fxanim(a,
11236                 o,
11237                 'motion',
11238                 .5,
11239                 'easeOut', after);
11240         });
11241         return this;
11242     },
11243     
11244         /**
11245          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11246          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11247          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11248          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11249          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11250          * Usage:
11251          *<pre><code>
11252 // default: slide the element out to the top
11253 el.slideOut();
11254
11255 // custom: slide the element out to the right with a 2-second duration
11256 el.slideOut('r', { duration: 2 });
11257
11258 // common config options shown with default values
11259 el.slideOut('t', {
11260     easing: 'easeOut',
11261     duration: .5,
11262     remove: false,
11263     useDisplay: false
11264 });
11265 </code></pre>
11266          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11267          * @param {Object} options (optional) Object literal with any of the Fx config options
11268          * @return {Roo.Element} The Element
11269          */
11270     slideOut : function(anchor, o){
11271         var el = this.getFxEl();
11272         o = o || {};
11273
11274         el.queueFx(o, function(){
11275
11276             anchor = anchor || "t";
11277
11278             // restore values after effect
11279             var r = this.getFxRestore();
11280             
11281             var b = this.getBox();
11282             // fixed size for slide
11283             this.setSize(b);
11284
11285             // wrap if needed
11286             var wrap = this.fxWrap(r.pos, o, "visible");
11287
11288             var st = this.dom.style;
11289             st.visibility = "visible";
11290             st.position = "absolute";
11291
11292             wrap.setSize(b);
11293
11294             var after = function(){
11295                 if(o.useDisplay){
11296                     el.setDisplayed(false);
11297                 }else{
11298                     el.hide();
11299                 }
11300
11301                 el.fxUnwrap(wrap, r.pos, o);
11302
11303                 st.width = r.width;
11304                 st.height = r.height;
11305
11306                 el.afterFx(o);
11307             };
11308
11309             var a, zero = {to: 0};
11310             switch(anchor.toLowerCase()){
11311                 case "t":
11312                     st.left = st.bottom = "0";
11313                     a = {height: zero};
11314                 break;
11315                 case "l":
11316                     st.right = st.top = "0";
11317                     a = {width: zero};
11318                 break;
11319                 case "r":
11320                     st.left = st.top = "0";
11321                     a = {width: zero, points: {to:[b.right, b.y]}};
11322                 break;
11323                 case "b":
11324                     st.left = st.top = "0";
11325                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11326                 break;
11327                 case "tl":
11328                     st.right = st.bottom = "0";
11329                     a = {width: zero, height: zero};
11330                 break;
11331                 case "bl":
11332                     st.right = st.top = "0";
11333                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11334                 break;
11335                 case "br":
11336                     st.left = st.top = "0";
11337                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11338                 break;
11339                 case "tr":
11340                     st.left = st.bottom = "0";
11341                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11342                 break;
11343             }
11344
11345             arguments.callee.anim = wrap.fxanim(a,
11346                 o,
11347                 'motion',
11348                 .5,
11349                 "easeOut", after);
11350         });
11351         return this;
11352     },
11353
11354         /**
11355          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11356          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11357          * The element must be removed from the DOM using the 'remove' config option if desired.
11358          * Usage:
11359          *<pre><code>
11360 // default
11361 el.puff();
11362
11363 // common config options shown with default values
11364 el.puff({
11365     easing: 'easeOut',
11366     duration: .5,
11367     remove: false,
11368     useDisplay: false
11369 });
11370 </code></pre>
11371          * @param {Object} options (optional) Object literal with any of the Fx config options
11372          * @return {Roo.Element} The Element
11373          */
11374     puff : function(o){
11375         var el = this.getFxEl();
11376         o = o || {};
11377
11378         el.queueFx(o, function(){
11379             this.clearOpacity();
11380             this.show();
11381
11382             // restore values after effect
11383             var r = this.getFxRestore();
11384             var st = this.dom.style;
11385
11386             var after = function(){
11387                 if(o.useDisplay){
11388                     el.setDisplayed(false);
11389                 }else{
11390                     el.hide();
11391                 }
11392
11393                 el.clearOpacity();
11394
11395                 el.setPositioning(r.pos);
11396                 st.width = r.width;
11397                 st.height = r.height;
11398                 st.fontSize = '';
11399                 el.afterFx(o);
11400             };
11401
11402             var width = this.getWidth();
11403             var height = this.getHeight();
11404
11405             arguments.callee.anim = this.fxanim({
11406                     width : {to: this.adjustWidth(width * 2)},
11407                     height : {to: this.adjustHeight(height * 2)},
11408                     points : {by: [-(width * .5), -(height * .5)]},
11409                     opacity : {to: 0},
11410                     fontSize: {to:200, unit: "%"}
11411                 },
11412                 o,
11413                 'motion',
11414                 .5,
11415                 "easeOut", after);
11416         });
11417         return this;
11418     },
11419
11420         /**
11421          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11422          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11423          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11424          * Usage:
11425          *<pre><code>
11426 // default
11427 el.switchOff();
11428
11429 // all config options shown with default values
11430 el.switchOff({
11431     easing: 'easeIn',
11432     duration: .3,
11433     remove: false,
11434     useDisplay: false
11435 });
11436 </code></pre>
11437          * @param {Object} options (optional) Object literal with any of the Fx config options
11438          * @return {Roo.Element} The Element
11439          */
11440     switchOff : function(o){
11441         var el = this.getFxEl();
11442         o = o || {};
11443
11444         el.queueFx(o, function(){
11445             this.clearOpacity();
11446             this.clip();
11447
11448             // restore values after effect
11449             var r = this.getFxRestore();
11450             var st = this.dom.style;
11451
11452             var after = function(){
11453                 if(o.useDisplay){
11454                     el.setDisplayed(false);
11455                 }else{
11456                     el.hide();
11457                 }
11458
11459                 el.clearOpacity();
11460                 el.setPositioning(r.pos);
11461                 st.width = r.width;
11462                 st.height = r.height;
11463
11464                 el.afterFx(o);
11465             };
11466
11467             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11468                 this.clearOpacity();
11469                 (function(){
11470                     this.fxanim({
11471                         height:{to:1},
11472                         points:{by:[0, this.getHeight() * .5]}
11473                     }, o, 'motion', 0.3, 'easeIn', after);
11474                 }).defer(100, this);
11475             });
11476         });
11477         return this;
11478     },
11479
11480     /**
11481      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11482      * changed using the "attr" config option) and then fading back to the original color. If no original
11483      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11484      * Usage:
11485 <pre><code>
11486 // default: highlight background to yellow
11487 el.highlight();
11488
11489 // custom: highlight foreground text to blue for 2 seconds
11490 el.highlight("0000ff", { attr: 'color', duration: 2 });
11491
11492 // common config options shown with default values
11493 el.highlight("ffff9c", {
11494     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11495     endColor: (current color) or "ffffff",
11496     easing: 'easeIn',
11497     duration: 1
11498 });
11499 </code></pre>
11500      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11501      * @param {Object} options (optional) Object literal with any of the Fx config options
11502      * @return {Roo.Element} The Element
11503      */ 
11504     highlight : function(color, o){
11505         var el = this.getFxEl();
11506         o = o || {};
11507
11508         el.queueFx(o, function(){
11509             color = color || "ffff9c";
11510             attr = o.attr || "backgroundColor";
11511
11512             this.clearOpacity();
11513             this.show();
11514
11515             var origColor = this.getColor(attr);
11516             var restoreColor = this.dom.style[attr];
11517             endColor = (o.endColor || origColor) || "ffffff";
11518
11519             var after = function(){
11520                 el.dom.style[attr] = restoreColor;
11521                 el.afterFx(o);
11522             };
11523
11524             var a = {};
11525             a[attr] = {from: color, to: endColor};
11526             arguments.callee.anim = this.fxanim(a,
11527                 o,
11528                 'color',
11529                 1,
11530                 'easeIn', after);
11531         });
11532         return this;
11533     },
11534
11535    /**
11536     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11537     * Usage:
11538 <pre><code>
11539 // default: a single light blue ripple
11540 el.frame();
11541
11542 // custom: 3 red ripples lasting 3 seconds total
11543 el.frame("ff0000", 3, { duration: 3 });
11544
11545 // common config options shown with default values
11546 el.frame("C3DAF9", 1, {
11547     duration: 1 //duration of entire animation (not each individual ripple)
11548     // Note: Easing is not configurable and will be ignored if included
11549 });
11550 </code></pre>
11551     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11552     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11553     * @param {Object} options (optional) Object literal with any of the Fx config options
11554     * @return {Roo.Element} The Element
11555     */
11556     frame : function(color, count, o){
11557         var el = this.getFxEl();
11558         o = o || {};
11559
11560         el.queueFx(o, function(){
11561             color = color || "#C3DAF9";
11562             if(color.length == 6){
11563                 color = "#" + color;
11564             }
11565             count = count || 1;
11566             duration = o.duration || 1;
11567             this.show();
11568
11569             var b = this.getBox();
11570             var animFn = function(){
11571                 var proxy = this.createProxy({
11572
11573                      style:{
11574                         visbility:"hidden",
11575                         position:"absolute",
11576                         "z-index":"35000", // yee haw
11577                         border:"0px solid " + color
11578                      }
11579                   });
11580                 var scale = Roo.isBorderBox ? 2 : 1;
11581                 proxy.animate({
11582                     top:{from:b.y, to:b.y - 20},
11583                     left:{from:b.x, to:b.x - 20},
11584                     borderWidth:{from:0, to:10},
11585                     opacity:{from:1, to:0},
11586                     height:{from:b.height, to:(b.height + (20*scale))},
11587                     width:{from:b.width, to:(b.width + (20*scale))}
11588                 }, duration, function(){
11589                     proxy.remove();
11590                 });
11591                 if(--count > 0){
11592                      animFn.defer((duration/2)*1000, this);
11593                 }else{
11594                     el.afterFx(o);
11595                 }
11596             };
11597             animFn.call(this);
11598         });
11599         return this;
11600     },
11601
11602    /**
11603     * Creates a pause before any subsequent queued effects begin.  If there are
11604     * no effects queued after the pause it will have no effect.
11605     * Usage:
11606 <pre><code>
11607 el.pause(1);
11608 </code></pre>
11609     * @param {Number} seconds The length of time to pause (in seconds)
11610     * @return {Roo.Element} The Element
11611     */
11612     pause : function(seconds){
11613         var el = this.getFxEl();
11614         var o = {};
11615
11616         el.queueFx(o, function(){
11617             setTimeout(function(){
11618                 el.afterFx(o);
11619             }, seconds * 1000);
11620         });
11621         return this;
11622     },
11623
11624    /**
11625     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11626     * using the "endOpacity" config option.
11627     * Usage:
11628 <pre><code>
11629 // default: fade in from opacity 0 to 100%
11630 el.fadeIn();
11631
11632 // custom: fade in from opacity 0 to 75% over 2 seconds
11633 el.fadeIn({ endOpacity: .75, duration: 2});
11634
11635 // common config options shown with default values
11636 el.fadeIn({
11637     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11638     easing: 'easeOut',
11639     duration: .5
11640 });
11641 </code></pre>
11642     * @param {Object} options (optional) Object literal with any of the Fx config options
11643     * @return {Roo.Element} The Element
11644     */
11645     fadeIn : function(o){
11646         var el = this.getFxEl();
11647         o = o || {};
11648         el.queueFx(o, function(){
11649             this.setOpacity(0);
11650             this.fixDisplay();
11651             this.dom.style.visibility = 'visible';
11652             var to = o.endOpacity || 1;
11653             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11654                 o, null, .5, "easeOut", function(){
11655                 if(to == 1){
11656                     this.clearOpacity();
11657                 }
11658                 el.afterFx(o);
11659             });
11660         });
11661         return this;
11662     },
11663
11664    /**
11665     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11666     * using the "endOpacity" config option.
11667     * Usage:
11668 <pre><code>
11669 // default: fade out from the element's current opacity to 0
11670 el.fadeOut();
11671
11672 // custom: fade out from the element's current opacity to 25% over 2 seconds
11673 el.fadeOut({ endOpacity: .25, duration: 2});
11674
11675 // common config options shown with default values
11676 el.fadeOut({
11677     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11678     easing: 'easeOut',
11679     duration: .5
11680     remove: false,
11681     useDisplay: false
11682 });
11683 </code></pre>
11684     * @param {Object} options (optional) Object literal with any of the Fx config options
11685     * @return {Roo.Element} The Element
11686     */
11687     fadeOut : function(o){
11688         var el = this.getFxEl();
11689         o = o || {};
11690         el.queueFx(o, function(){
11691             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11692                 o, null, .5, "easeOut", function(){
11693                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11694                      this.dom.style.display = "none";
11695                 }else{
11696                      this.dom.style.visibility = "hidden";
11697                 }
11698                 this.clearOpacity();
11699                 el.afterFx(o);
11700             });
11701         });
11702         return this;
11703     },
11704
11705    /**
11706     * Animates the transition of an element's dimensions from a starting height/width
11707     * to an ending height/width.
11708     * Usage:
11709 <pre><code>
11710 // change height and width to 100x100 pixels
11711 el.scale(100, 100);
11712
11713 // common config options shown with default values.  The height and width will default to
11714 // the element's existing values if passed as null.
11715 el.scale(
11716     [element's width],
11717     [element's height], {
11718     easing: 'easeOut',
11719     duration: .35
11720 });
11721 </code></pre>
11722     * @param {Number} width  The new width (pass undefined to keep the original width)
11723     * @param {Number} height  The new height (pass undefined to keep the original height)
11724     * @param {Object} options (optional) Object literal with any of the Fx config options
11725     * @return {Roo.Element} The Element
11726     */
11727     scale : function(w, h, o){
11728         this.shift(Roo.apply({}, o, {
11729             width: w,
11730             height: h
11731         }));
11732         return this;
11733     },
11734
11735    /**
11736     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11737     * Any of these properties not specified in the config object will not be changed.  This effect 
11738     * requires that at least one new dimension, position or opacity setting must be passed in on
11739     * the config object in order for the function to have any effect.
11740     * Usage:
11741 <pre><code>
11742 // slide the element horizontally to x position 200 while changing the height and opacity
11743 el.shift({ x: 200, height: 50, opacity: .8 });
11744
11745 // common config options shown with default values.
11746 el.shift({
11747     width: [element's width],
11748     height: [element's height],
11749     x: [element's x position],
11750     y: [element's y position],
11751     opacity: [element's opacity],
11752     easing: 'easeOut',
11753     duration: .35
11754 });
11755 </code></pre>
11756     * @param {Object} options  Object literal with any of the Fx config options
11757     * @return {Roo.Element} The Element
11758     */
11759     shift : function(o){
11760         var el = this.getFxEl();
11761         o = o || {};
11762         el.queueFx(o, function(){
11763             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11764             if(w !== undefined){
11765                 a.width = {to: this.adjustWidth(w)};
11766             }
11767             if(h !== undefined){
11768                 a.height = {to: this.adjustHeight(h)};
11769             }
11770             if(x !== undefined || y !== undefined){
11771                 a.points = {to: [
11772                     x !== undefined ? x : this.getX(),
11773                     y !== undefined ? y : this.getY()
11774                 ]};
11775             }
11776             if(op !== undefined){
11777                 a.opacity = {to: op};
11778             }
11779             if(o.xy !== undefined){
11780                 a.points = {to: o.xy};
11781             }
11782             arguments.callee.anim = this.fxanim(a,
11783                 o, 'motion', .35, "easeOut", function(){
11784                 el.afterFx(o);
11785             });
11786         });
11787         return this;
11788     },
11789
11790         /**
11791          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11792          * ending point of the effect.
11793          * Usage:
11794          *<pre><code>
11795 // default: slide the element downward while fading out
11796 el.ghost();
11797
11798 // custom: slide the element out to the right with a 2-second duration
11799 el.ghost('r', { duration: 2 });
11800
11801 // common config options shown with default values
11802 el.ghost('b', {
11803     easing: 'easeOut',
11804     duration: .5
11805     remove: false,
11806     useDisplay: false
11807 });
11808 </code></pre>
11809          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11810          * @param {Object} options (optional) Object literal with any of the Fx config options
11811          * @return {Roo.Element} The Element
11812          */
11813     ghost : function(anchor, o){
11814         var el = this.getFxEl();
11815         o = o || {};
11816
11817         el.queueFx(o, function(){
11818             anchor = anchor || "b";
11819
11820             // restore values after effect
11821             var r = this.getFxRestore();
11822             var w = this.getWidth(),
11823                 h = this.getHeight();
11824
11825             var st = this.dom.style;
11826
11827             var after = function(){
11828                 if(o.useDisplay){
11829                     el.setDisplayed(false);
11830                 }else{
11831                     el.hide();
11832                 }
11833
11834                 el.clearOpacity();
11835                 el.setPositioning(r.pos);
11836                 st.width = r.width;
11837                 st.height = r.height;
11838
11839                 el.afterFx(o);
11840             };
11841
11842             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11843             switch(anchor.toLowerCase()){
11844                 case "t":
11845                     pt.by = [0, -h];
11846                 break;
11847                 case "l":
11848                     pt.by = [-w, 0];
11849                 break;
11850                 case "r":
11851                     pt.by = [w, 0];
11852                 break;
11853                 case "b":
11854                     pt.by = [0, h];
11855                 break;
11856                 case "tl":
11857                     pt.by = [-w, -h];
11858                 break;
11859                 case "bl":
11860                     pt.by = [-w, h];
11861                 break;
11862                 case "br":
11863                     pt.by = [w, h];
11864                 break;
11865                 case "tr":
11866                     pt.by = [w, -h];
11867                 break;
11868             }
11869
11870             arguments.callee.anim = this.fxanim(a,
11871                 o,
11872                 'motion',
11873                 .5,
11874                 "easeOut", after);
11875         });
11876         return this;
11877     },
11878
11879         /**
11880          * Ensures that all effects queued after syncFx is called on the element are
11881          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11882          * @return {Roo.Element} The Element
11883          */
11884     syncFx : function(){
11885         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11886             block : false,
11887             concurrent : true,
11888             stopFx : false
11889         });
11890         return this;
11891     },
11892
11893         /**
11894          * Ensures that all effects queued after sequenceFx is called on the element are
11895          * run in sequence.  This is the opposite of {@link #syncFx}.
11896          * @return {Roo.Element} The Element
11897          */
11898     sequenceFx : function(){
11899         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11900             block : false,
11901             concurrent : false,
11902             stopFx : false
11903         });
11904         return this;
11905     },
11906
11907         /* @private */
11908     nextFx : function(){
11909         var ef = this.fxQueue[0];
11910         if(ef){
11911             ef.call(this);
11912         }
11913     },
11914
11915         /**
11916          * Returns true if the element has any effects actively running or queued, else returns false.
11917          * @return {Boolean} True if element has active effects, else false
11918          */
11919     hasActiveFx : function(){
11920         return this.fxQueue && this.fxQueue[0];
11921     },
11922
11923         /**
11924          * Stops any running effects and clears the element's internal effects queue if it contains
11925          * any additional effects that haven't started yet.
11926          * @return {Roo.Element} The Element
11927          */
11928     stopFx : function(){
11929         if(this.hasActiveFx()){
11930             var cur = this.fxQueue[0];
11931             if(cur && cur.anim && cur.anim.isAnimated()){
11932                 this.fxQueue = [cur]; // clear out others
11933                 cur.anim.stop(true);
11934             }
11935         }
11936         return this;
11937     },
11938
11939         /* @private */
11940     beforeFx : function(o){
11941         if(this.hasActiveFx() && !o.concurrent){
11942            if(o.stopFx){
11943                this.stopFx();
11944                return true;
11945            }
11946            return false;
11947         }
11948         return true;
11949     },
11950
11951         /**
11952          * Returns true if the element is currently blocking so that no other effect can be queued
11953          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11954          * used to ensure that an effect initiated by a user action runs to completion prior to the
11955          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11956          * @return {Boolean} True if blocking, else false
11957          */
11958     hasFxBlock : function(){
11959         var q = this.fxQueue;
11960         return q && q[0] && q[0].block;
11961     },
11962
11963         /* @private */
11964     queueFx : function(o, fn){
11965         if(!this.fxQueue){
11966             this.fxQueue = [];
11967         }
11968         if(!this.hasFxBlock()){
11969             Roo.applyIf(o, this.fxDefaults);
11970             if(!o.concurrent){
11971                 var run = this.beforeFx(o);
11972                 fn.block = o.block;
11973                 this.fxQueue.push(fn);
11974                 if(run){
11975                     this.nextFx();
11976                 }
11977             }else{
11978                 fn.call(this);
11979             }
11980         }
11981         return this;
11982     },
11983
11984         /* @private */
11985     fxWrap : function(pos, o, vis){
11986         var wrap;
11987         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11988             var wrapXY;
11989             if(o.fixPosition){
11990                 wrapXY = this.getXY();
11991             }
11992             var div = document.createElement("div");
11993             div.style.visibility = vis;
11994             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11995             wrap.setPositioning(pos);
11996             if(wrap.getStyle("position") == "static"){
11997                 wrap.position("relative");
11998             }
11999             this.clearPositioning('auto');
12000             wrap.clip();
12001             wrap.dom.appendChild(this.dom);
12002             if(wrapXY){
12003                 wrap.setXY(wrapXY);
12004             }
12005         }
12006         return wrap;
12007     },
12008
12009         /* @private */
12010     fxUnwrap : function(wrap, pos, o){
12011         this.clearPositioning();
12012         this.setPositioning(pos);
12013         if(!o.wrap){
12014             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12015             wrap.remove();
12016         }
12017     },
12018
12019         /* @private */
12020     getFxRestore : function(){
12021         var st = this.dom.style;
12022         return {pos: this.getPositioning(), width: st.width, height : st.height};
12023     },
12024
12025         /* @private */
12026     afterFx : function(o){
12027         if(o.afterStyle){
12028             this.applyStyles(o.afterStyle);
12029         }
12030         if(o.afterCls){
12031             this.addClass(o.afterCls);
12032         }
12033         if(o.remove === true){
12034             this.remove();
12035         }
12036         Roo.callback(o.callback, o.scope, [this]);
12037         if(!o.concurrent){
12038             this.fxQueue.shift();
12039             this.nextFx();
12040         }
12041     },
12042
12043         /* @private */
12044     getFxEl : function(){ // support for composite element fx
12045         return Roo.get(this.dom);
12046     },
12047
12048         /* @private */
12049     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12050         animType = animType || 'run';
12051         opt = opt || {};
12052         var anim = Roo.lib.Anim[animType](
12053             this.dom, args,
12054             (opt.duration || defaultDur) || .35,
12055             (opt.easing || defaultEase) || 'easeOut',
12056             function(){
12057                 Roo.callback(cb, this);
12058             },
12059             this
12060         );
12061         opt.anim = anim;
12062         return anim;
12063     }
12064 };
12065
12066 // backwords compat
12067 Roo.Fx.resize = Roo.Fx.scale;
12068
12069 //When included, Roo.Fx is automatically applied to Element so that all basic
12070 //effects are available directly via the Element API
12071 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12072  * Based on:
12073  * Ext JS Library 1.1.1
12074  * Copyright(c) 2006-2007, Ext JS, LLC.
12075  *
12076  * Originally Released Under LGPL - original licence link has changed is not relivant.
12077  *
12078  * Fork - LGPL
12079  * <script type="text/javascript">
12080  */
12081
12082
12083 /**
12084  * @class Roo.CompositeElement
12085  * Standard composite class. Creates a Roo.Element for every element in the collection.
12086  * <br><br>
12087  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12088  * actions will be performed on all the elements in this collection.</b>
12089  * <br><br>
12090  * All methods return <i>this</i> and can be chained.
12091  <pre><code>
12092  var els = Roo.select("#some-el div.some-class", true);
12093  // or select directly from an existing element
12094  var el = Roo.get('some-el');
12095  el.select('div.some-class', true);
12096
12097  els.setWidth(100); // all elements become 100 width
12098  els.hide(true); // all elements fade out and hide
12099  // or
12100  els.setWidth(100).hide(true);
12101  </code></pre>
12102  */
12103 Roo.CompositeElement = function(els){
12104     this.elements = [];
12105     this.addElements(els);
12106 };
12107 Roo.CompositeElement.prototype = {
12108     isComposite: true,
12109     addElements : function(els){
12110         if(!els) {
12111             return this;
12112         }
12113         if(typeof els == "string"){
12114             els = Roo.Element.selectorFunction(els);
12115         }
12116         var yels = this.elements;
12117         var index = yels.length-1;
12118         for(var i = 0, len = els.length; i < len; i++) {
12119                 yels[++index] = Roo.get(els[i]);
12120         }
12121         return this;
12122     },
12123
12124     /**
12125     * Clears this composite and adds the elements returned by the passed selector.
12126     * @param {String/Array} els A string CSS selector, an array of elements or an element
12127     * @return {CompositeElement} this
12128     */
12129     fill : function(els){
12130         this.elements = [];
12131         this.add(els);
12132         return this;
12133     },
12134
12135     /**
12136     * Filters this composite to only elements that match the passed selector.
12137     * @param {String} selector A string CSS selector
12138     * @param {Boolean} inverse return inverse filter (not matches)
12139     * @return {CompositeElement} this
12140     */
12141     filter : function(selector, inverse){
12142         var els = [];
12143         inverse = inverse || false;
12144         this.each(function(el){
12145             var match = inverse ? !el.is(selector) : el.is(selector);
12146             if(match){
12147                 els[els.length] = el.dom;
12148             }
12149         });
12150         this.fill(els);
12151         return this;
12152     },
12153
12154     invoke : function(fn, args){
12155         var els = this.elements;
12156         for(var i = 0, len = els.length; i < len; i++) {
12157                 Roo.Element.prototype[fn].apply(els[i], args);
12158         }
12159         return this;
12160     },
12161     /**
12162     * Adds elements to this composite.
12163     * @param {String/Array} els A string CSS selector, an array of elements or an element
12164     * @return {CompositeElement} this
12165     */
12166     add : function(els){
12167         if(typeof els == "string"){
12168             this.addElements(Roo.Element.selectorFunction(els));
12169         }else if(els.length !== undefined){
12170             this.addElements(els);
12171         }else{
12172             this.addElements([els]);
12173         }
12174         return this;
12175     },
12176     /**
12177     * Calls the passed function passing (el, this, index) for each element in this composite.
12178     * @param {Function} fn The function to call
12179     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12180     * @return {CompositeElement} this
12181     */
12182     each : function(fn, scope){
12183         var els = this.elements;
12184         for(var i = 0, len = els.length; i < len; i++){
12185             if(fn.call(scope || els[i], els[i], this, i) === false) {
12186                 break;
12187             }
12188         }
12189         return this;
12190     },
12191
12192     /**
12193      * Returns the Element object at the specified index
12194      * @param {Number} index
12195      * @return {Roo.Element}
12196      */
12197     item : function(index){
12198         return this.elements[index] || null;
12199     },
12200
12201     /**
12202      * Returns the first Element
12203      * @return {Roo.Element}
12204      */
12205     first : function(){
12206         return this.item(0);
12207     },
12208
12209     /**
12210      * Returns the last Element
12211      * @return {Roo.Element}
12212      */
12213     last : function(){
12214         return this.item(this.elements.length-1);
12215     },
12216
12217     /**
12218      * Returns the number of elements in this composite
12219      * @return Number
12220      */
12221     getCount : function(){
12222         return this.elements.length;
12223     },
12224
12225     /**
12226      * Returns true if this composite contains the passed element
12227      * @return Boolean
12228      */
12229     contains : function(el){
12230         return this.indexOf(el) !== -1;
12231     },
12232
12233     /**
12234      * Returns true if this composite contains the passed element
12235      * @return Boolean
12236      */
12237     indexOf : function(el){
12238         return this.elements.indexOf(Roo.get(el));
12239     },
12240
12241
12242     /**
12243     * Removes the specified element(s).
12244     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12245     * or an array of any of those.
12246     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12247     * @return {CompositeElement} this
12248     */
12249     removeElement : function(el, removeDom){
12250         if(el instanceof Array){
12251             for(var i = 0, len = el.length; i < len; i++){
12252                 this.removeElement(el[i]);
12253             }
12254             return this;
12255         }
12256         var index = typeof el == 'number' ? el : this.indexOf(el);
12257         if(index !== -1){
12258             if(removeDom){
12259                 var d = this.elements[index];
12260                 if(d.dom){
12261                     d.remove();
12262                 }else{
12263                     d.parentNode.removeChild(d);
12264                 }
12265             }
12266             this.elements.splice(index, 1);
12267         }
12268         return this;
12269     },
12270
12271     /**
12272     * Replaces the specified element with the passed element.
12273     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12274     * to replace.
12275     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12276     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12277     * @return {CompositeElement} this
12278     */
12279     replaceElement : function(el, replacement, domReplace){
12280         var index = typeof el == 'number' ? el : this.indexOf(el);
12281         if(index !== -1){
12282             if(domReplace){
12283                 this.elements[index].replaceWith(replacement);
12284             }else{
12285                 this.elements.splice(index, 1, Roo.get(replacement))
12286             }
12287         }
12288         return this;
12289     },
12290
12291     /**
12292      * Removes all elements.
12293      */
12294     clear : function(){
12295         this.elements = [];
12296     }
12297 };
12298 (function(){
12299     Roo.CompositeElement.createCall = function(proto, fnName){
12300         if(!proto[fnName]){
12301             proto[fnName] = function(){
12302                 return this.invoke(fnName, arguments);
12303             };
12304         }
12305     };
12306     for(var fnName in Roo.Element.prototype){
12307         if(typeof Roo.Element.prototype[fnName] == "function"){
12308             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12309         }
12310     };
12311 })();
12312 /*
12313  * Based on:
12314  * Ext JS Library 1.1.1
12315  * Copyright(c) 2006-2007, Ext JS, LLC.
12316  *
12317  * Originally Released Under LGPL - original licence link has changed is not relivant.
12318  *
12319  * Fork - LGPL
12320  * <script type="text/javascript">
12321  */
12322
12323 /**
12324  * @class Roo.CompositeElementLite
12325  * @extends Roo.CompositeElement
12326  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12327  <pre><code>
12328  var els = Roo.select("#some-el div.some-class");
12329  // or select directly from an existing element
12330  var el = Roo.get('some-el');
12331  el.select('div.some-class');
12332
12333  els.setWidth(100); // all elements become 100 width
12334  els.hide(true); // all elements fade out and hide
12335  // or
12336  els.setWidth(100).hide(true);
12337  </code></pre><br><br>
12338  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12339  * actions will be performed on all the elements in this collection.</b>
12340  */
12341 Roo.CompositeElementLite = function(els){
12342     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12343     this.el = new Roo.Element.Flyweight();
12344 };
12345 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12346     addElements : function(els){
12347         if(els){
12348             if(els instanceof Array){
12349                 this.elements = this.elements.concat(els);
12350             }else{
12351                 var yels = this.elements;
12352                 var index = yels.length-1;
12353                 for(var i = 0, len = els.length; i < len; i++) {
12354                     yels[++index] = els[i];
12355                 }
12356             }
12357         }
12358         return this;
12359     },
12360     invoke : function(fn, args){
12361         var els = this.elements;
12362         var el = this.el;
12363         for(var i = 0, len = els.length; i < len; i++) {
12364             el.dom = els[i];
12365                 Roo.Element.prototype[fn].apply(el, args);
12366         }
12367         return this;
12368     },
12369     /**
12370      * Returns a flyweight Element of the dom element object at the specified index
12371      * @param {Number} index
12372      * @return {Roo.Element}
12373      */
12374     item : function(index){
12375         if(!this.elements[index]){
12376             return null;
12377         }
12378         this.el.dom = this.elements[index];
12379         return this.el;
12380     },
12381
12382     // fixes scope with flyweight
12383     addListener : function(eventName, handler, scope, opt){
12384         var els = this.elements;
12385         for(var i = 0, len = els.length; i < len; i++) {
12386             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12387         }
12388         return this;
12389     },
12390
12391     /**
12392     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12393     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12394     * a reference to the dom node, use el.dom.</b>
12395     * @param {Function} fn The function to call
12396     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12397     * @return {CompositeElement} this
12398     */
12399     each : function(fn, scope){
12400         var els = this.elements;
12401         var el = this.el;
12402         for(var i = 0, len = els.length; i < len; i++){
12403             el.dom = els[i];
12404                 if(fn.call(scope || el, el, this, i) === false){
12405                 break;
12406             }
12407         }
12408         return this;
12409     },
12410
12411     indexOf : function(el){
12412         return this.elements.indexOf(Roo.getDom(el));
12413     },
12414
12415     replaceElement : function(el, replacement, domReplace){
12416         var index = typeof el == 'number' ? el : this.indexOf(el);
12417         if(index !== -1){
12418             replacement = Roo.getDom(replacement);
12419             if(domReplace){
12420                 var d = this.elements[index];
12421                 d.parentNode.insertBefore(replacement, d);
12422                 d.parentNode.removeChild(d);
12423             }
12424             this.elements.splice(index, 1, replacement);
12425         }
12426         return this;
12427     }
12428 });
12429 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12430
12431 /*
12432  * Based on:
12433  * Ext JS Library 1.1.1
12434  * Copyright(c) 2006-2007, Ext JS, LLC.
12435  *
12436  * Originally Released Under LGPL - original licence link has changed is not relivant.
12437  *
12438  * Fork - LGPL
12439  * <script type="text/javascript">
12440  */
12441
12442  
12443
12444 /**
12445  * @class Roo.data.Connection
12446  * @extends Roo.util.Observable
12447  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12448  * either to a configured URL, or to a URL specified at request time. 
12449  * 
12450  * Requests made by this class are asynchronous, and will return immediately. No data from
12451  * the server will be available to the statement immediately following the {@link #request} call.
12452  * To process returned data, use a callback in the request options object, or an event listener.
12453  * 
12454  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12455  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12456  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12457  * property and, if present, the IFRAME's XML document as the responseXML property.
12458  * 
12459  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12460  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12461  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12462  * standard DOM methods.
12463  * @constructor
12464  * @param {Object} config a configuration object.
12465  */
12466 Roo.data.Connection = function(config){
12467     Roo.apply(this, config);
12468     this.addEvents({
12469         /**
12470          * @event beforerequest
12471          * Fires before a network request is made to retrieve a data object.
12472          * @param {Connection} conn This Connection object.
12473          * @param {Object} options The options config object passed to the {@link #request} method.
12474          */
12475         "beforerequest" : true,
12476         /**
12477          * @event requestcomplete
12478          * Fires if the request was successfully completed.
12479          * @param {Connection} conn This Connection object.
12480          * @param {Object} response The XHR object containing the response data.
12481          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12482          * @param {Object} options The options config object passed to the {@link #request} method.
12483          */
12484         "requestcomplete" : true,
12485         /**
12486          * @event requestexception
12487          * Fires if an error HTTP status was returned from the server.
12488          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12489          * @param {Connection} conn This Connection object.
12490          * @param {Object} response The XHR object containing the response data.
12491          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12492          * @param {Object} options The options config object passed to the {@link #request} method.
12493          */
12494         "requestexception" : true
12495     });
12496     Roo.data.Connection.superclass.constructor.call(this);
12497 };
12498
12499 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12500     /**
12501      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12502      */
12503     /**
12504      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12505      * extra parameters to each request made by this object. (defaults to undefined)
12506      */
12507     /**
12508      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12509      *  to each request made by this object. (defaults to undefined)
12510      */
12511     /**
12512      * @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)
12513      */
12514     /**
12515      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12516      */
12517     timeout : 30000,
12518     /**
12519      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12520      * @type Boolean
12521      */
12522     autoAbort:false,
12523
12524     /**
12525      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12526      * @type Boolean
12527      */
12528     disableCaching: true,
12529
12530     /**
12531      * Sends an HTTP request to a remote server.
12532      * @param {Object} options An object which may contain the following properties:<ul>
12533      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12534      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12535      * request, a url encoded string or a function to call to get either.</li>
12536      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12537      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12538      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12539      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12540      * <li>options {Object} The parameter to the request call.</li>
12541      * <li>success {Boolean} True if the request succeeded.</li>
12542      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12543      * </ul></li>
12544      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12545      * The callback is passed the following parameters:<ul>
12546      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12547      * <li>options {Object} The parameter to the request call.</li>
12548      * </ul></li>
12549      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12550      * The callback is passed the following parameters:<ul>
12551      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12552      * <li>options {Object} The parameter to the request call.</li>
12553      * </ul></li>
12554      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12555      * for the callback function. Defaults to the browser window.</li>
12556      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12557      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12558      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12559      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12560      * params for the post data. Any params will be appended to the URL.</li>
12561      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12562      * </ul>
12563      * @return {Number} transactionId
12564      */
12565     request : function(o){
12566         if(this.fireEvent("beforerequest", this, o) !== false){
12567             var p = o.params;
12568
12569             if(typeof p == "function"){
12570                 p = p.call(o.scope||window, o);
12571             }
12572             if(typeof p == "object"){
12573                 p = Roo.urlEncode(o.params);
12574             }
12575             if(this.extraParams){
12576                 var extras = Roo.urlEncode(this.extraParams);
12577                 p = p ? (p + '&' + extras) : extras;
12578             }
12579
12580             var url = o.url || this.url;
12581             if(typeof url == 'function'){
12582                 url = url.call(o.scope||window, o);
12583             }
12584
12585             if(o.form){
12586                 var form = Roo.getDom(o.form);
12587                 url = url || form.action;
12588
12589                 var enctype = form.getAttribute("enctype");
12590                 
12591                 if (o.formData) {
12592                     return this.doFormDataUpload(o, url);
12593                 }
12594                 
12595                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12596                     return this.doFormUpload(o, p, url);
12597                 }
12598                 var f = Roo.lib.Ajax.serializeForm(form);
12599                 p = p ? (p + '&' + f) : f;
12600             }
12601             
12602             if (!o.form && o.formData) {
12603                 o.formData = o.formData === true ? new FormData() : o.formData;
12604                 for (var k in o.params) {
12605                     o.formData.append(k,o.params[k]);
12606                 }
12607                     
12608                 return this.doFormDataUpload(o, url);
12609             }
12610             
12611
12612             var hs = o.headers;
12613             if(this.defaultHeaders){
12614                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12615                 if(!o.headers){
12616                     o.headers = hs;
12617                 }
12618             }
12619
12620             var cb = {
12621                 success: this.handleResponse,
12622                 failure: this.handleFailure,
12623                 scope: this,
12624                 argument: {options: o},
12625                 timeout : o.timeout || this.timeout
12626             };
12627
12628             var method = o.method||this.method||(p ? "POST" : "GET");
12629
12630             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12631                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12632             }
12633
12634             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12635                 if(o.autoAbort){
12636                     this.abort();
12637                 }
12638             }else if(this.autoAbort !== false){
12639                 this.abort();
12640             }
12641
12642             if((method == 'GET' && p) || o.xmlData){
12643                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12644                 p = '';
12645             }
12646             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12647             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12648             Roo.lib.Ajax.useDefaultHeader == true;
12649             return this.transId;
12650         }else{
12651             Roo.callback(o.callback, o.scope, [o, null, null]);
12652             return null;
12653         }
12654     },
12655
12656     /**
12657      * Determine whether this object has a request outstanding.
12658      * @param {Number} transactionId (Optional) defaults to the last transaction
12659      * @return {Boolean} True if there is an outstanding request.
12660      */
12661     isLoading : function(transId){
12662         if(transId){
12663             return Roo.lib.Ajax.isCallInProgress(transId);
12664         }else{
12665             return this.transId ? true : false;
12666         }
12667     },
12668
12669     /**
12670      * Aborts any outstanding request.
12671      * @param {Number} transactionId (Optional) defaults to the last transaction
12672      */
12673     abort : function(transId){
12674         if(transId || this.isLoading()){
12675             Roo.lib.Ajax.abort(transId || this.transId);
12676         }
12677     },
12678
12679     // private
12680     handleResponse : function(response){
12681         this.transId = false;
12682         var options = response.argument.options;
12683         response.argument = options ? options.argument : null;
12684         this.fireEvent("requestcomplete", this, response, options);
12685         Roo.callback(options.success, options.scope, [response, options]);
12686         Roo.callback(options.callback, options.scope, [options, true, response]);
12687     },
12688
12689     // private
12690     handleFailure : function(response, e){
12691         this.transId = false;
12692         var options = response.argument.options;
12693         response.argument = options ? options.argument : null;
12694         this.fireEvent("requestexception", this, response, options, e);
12695         Roo.callback(options.failure, options.scope, [response, options]);
12696         Roo.callback(options.callback, options.scope, [options, false, response]);
12697     },
12698
12699     // private
12700     doFormUpload : function(o, ps, url){
12701         var id = Roo.id();
12702         var frame = document.createElement('iframe');
12703         frame.id = id;
12704         frame.name = id;
12705         frame.className = 'x-hidden';
12706         if(Roo.isIE){
12707             frame.src = Roo.SSL_SECURE_URL;
12708         }
12709         document.body.appendChild(frame);
12710
12711         if(Roo.isIE){
12712            document.frames[id].name = id;
12713         }
12714
12715         var form = Roo.getDom(o.form);
12716         form.target = id;
12717         form.method = 'POST';
12718         form.enctype = form.encoding = 'multipart/form-data';
12719         if(url){
12720             form.action = url;
12721         }
12722
12723         var hiddens, hd;
12724         if(ps){ // add dynamic params
12725             hiddens = [];
12726             ps = Roo.urlDecode(ps, false);
12727             for(var k in ps){
12728                 if(ps.hasOwnProperty(k)){
12729                     hd = document.createElement('input');
12730                     hd.type = 'hidden';
12731                     hd.name = k;
12732                     hd.value = ps[k];
12733                     form.appendChild(hd);
12734                     hiddens.push(hd);
12735                 }
12736             }
12737         }
12738
12739         function cb(){
12740             var r = {  // bogus response object
12741                 responseText : '',
12742                 responseXML : null
12743             };
12744
12745             r.argument = o ? o.argument : null;
12746
12747             try { //
12748                 var doc;
12749                 if(Roo.isIE){
12750                     doc = frame.contentWindow.document;
12751                 }else {
12752                     doc = (frame.contentDocument || window.frames[id].document);
12753                 }
12754                 if(doc && doc.body){
12755                     r.responseText = doc.body.innerHTML;
12756                 }
12757                 if(doc && doc.XMLDocument){
12758                     r.responseXML = doc.XMLDocument;
12759                 }else {
12760                     r.responseXML = doc;
12761                 }
12762             }
12763             catch(e) {
12764                 // ignore
12765             }
12766
12767             Roo.EventManager.removeListener(frame, 'load', cb, this);
12768
12769             this.fireEvent("requestcomplete", this, r, o);
12770             Roo.callback(o.success, o.scope, [r, o]);
12771             Roo.callback(o.callback, o.scope, [o, true, r]);
12772
12773             setTimeout(function(){document.body.removeChild(frame);}, 100);
12774         }
12775
12776         Roo.EventManager.on(frame, 'load', cb, this);
12777         form.submit();
12778
12779         if(hiddens){ // remove dynamic params
12780             for(var i = 0, len = hiddens.length; i < len; i++){
12781                 form.removeChild(hiddens[i]);
12782             }
12783         }
12784     },
12785     // this is a 'formdata version???'
12786     
12787     
12788     doFormDataUpload : function(o,  url)
12789     {
12790         var formData;
12791         if (o.form) {
12792             var form =  Roo.getDom(o.form);
12793             form.enctype = form.encoding = 'multipart/form-data';
12794             formData = o.formData === true ? new FormData(form) : o.formData;
12795         } else {
12796             formData = o.formData === true ? new FormData() : o.formData;
12797         }
12798         
12799       
12800         var cb = {
12801             success: this.handleResponse,
12802             failure: this.handleFailure,
12803             scope: this,
12804             argument: {options: o},
12805             timeout : o.timeout || this.timeout
12806         };
12807  
12808         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12809             if(o.autoAbort){
12810                 this.abort();
12811             }
12812         }else if(this.autoAbort !== false){
12813             this.abort();
12814         }
12815
12816         //Roo.lib.Ajax.defaultPostHeader = null;
12817         Roo.lib.Ajax.useDefaultHeader = false;
12818         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12819         Roo.lib.Ajax.useDefaultHeader = true;
12820  
12821          
12822     }
12823     
12824 });
12825 /*
12826  * Based on:
12827  * Ext JS Library 1.1.1
12828  * Copyright(c) 2006-2007, Ext JS, LLC.
12829  *
12830  * Originally Released Under LGPL - original licence link has changed is not relivant.
12831  *
12832  * Fork - LGPL
12833  * <script type="text/javascript">
12834  */
12835  
12836 /**
12837  * Global Ajax request class.
12838  * 
12839  * @class Roo.Ajax
12840  * @extends Roo.data.Connection
12841  * @static
12842  * 
12843  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12844  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12845  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12846  * @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)
12847  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12848  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12849  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12850  */
12851 Roo.Ajax = new Roo.data.Connection({
12852     // fix up the docs
12853     /**
12854      * @scope Roo.Ajax
12855      * @type {Boolear} 
12856      */
12857     autoAbort : false,
12858
12859     /**
12860      * Serialize the passed form into a url encoded string
12861      * @scope Roo.Ajax
12862      * @param {String/HTMLElement} form
12863      * @return {String}
12864      */
12865     serializeForm : function(form){
12866         return Roo.lib.Ajax.serializeForm(form);
12867     }
12868 });/*
12869  * Based on:
12870  * Ext JS Library 1.1.1
12871  * Copyright(c) 2006-2007, Ext JS, LLC.
12872  *
12873  * Originally Released Under LGPL - original licence link has changed is not relivant.
12874  *
12875  * Fork - LGPL
12876  * <script type="text/javascript">
12877  */
12878
12879  
12880 /**
12881  * @class Roo.UpdateManager
12882  * @extends Roo.util.Observable
12883  * Provides AJAX-style update for Element object.<br><br>
12884  * Usage:<br>
12885  * <pre><code>
12886  * // Get it from a Roo.Element object
12887  * var el = Roo.get("foo");
12888  * var mgr = el.getUpdateManager();
12889  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12890  * ...
12891  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12892  * <br>
12893  * // or directly (returns the same UpdateManager instance)
12894  * var mgr = new Roo.UpdateManager("myElementId");
12895  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12896  * mgr.on("update", myFcnNeedsToKnow);
12897  * <br>
12898    // short handed call directly from the element object
12899    Roo.get("foo").load({
12900         url: "bar.php",
12901         scripts:true,
12902         params: "for=bar",
12903         text: "Loading Foo..."
12904    });
12905  * </code></pre>
12906  * @constructor
12907  * Create new UpdateManager directly.
12908  * @param {String/HTMLElement/Roo.Element} el The element to update
12909  * @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).
12910  */
12911 Roo.UpdateManager = function(el, forceNew){
12912     el = Roo.get(el);
12913     if(!forceNew && el.updateManager){
12914         return el.updateManager;
12915     }
12916     /**
12917      * The Element object
12918      * @type Roo.Element
12919      */
12920     this.el = el;
12921     /**
12922      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12923      * @type String
12924      */
12925     this.defaultUrl = null;
12926
12927     this.addEvents({
12928         /**
12929          * @event beforeupdate
12930          * Fired before an update is made, return false from your handler and the update is cancelled.
12931          * @param {Roo.Element} el
12932          * @param {String/Object/Function} url
12933          * @param {String/Object} params
12934          */
12935         "beforeupdate": true,
12936         /**
12937          * @event update
12938          * Fired after successful update is made.
12939          * @param {Roo.Element} el
12940          * @param {Object} oResponseObject The response Object
12941          */
12942         "update": true,
12943         /**
12944          * @event failure
12945          * Fired on update failure.
12946          * @param {Roo.Element} el
12947          * @param {Object} oResponseObject The response Object
12948          */
12949         "failure": true
12950     });
12951     var d = Roo.UpdateManager.defaults;
12952     /**
12953      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12954      * @type String
12955      */
12956     this.sslBlankUrl = d.sslBlankUrl;
12957     /**
12958      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12959      * @type Boolean
12960      */
12961     this.disableCaching = d.disableCaching;
12962     /**
12963      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12964      * @type String
12965      */
12966     this.indicatorText = d.indicatorText;
12967     /**
12968      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12969      * @type String
12970      */
12971     this.showLoadIndicator = d.showLoadIndicator;
12972     /**
12973      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12974      * @type Number
12975      */
12976     this.timeout = d.timeout;
12977
12978     /**
12979      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12980      * @type Boolean
12981      */
12982     this.loadScripts = d.loadScripts;
12983
12984     /**
12985      * Transaction object of current executing transaction
12986      */
12987     this.transaction = null;
12988
12989     /**
12990      * @private
12991      */
12992     this.autoRefreshProcId = null;
12993     /**
12994      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12995      * @type Function
12996      */
12997     this.refreshDelegate = this.refresh.createDelegate(this);
12998     /**
12999      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13000      * @type Function
13001      */
13002     this.updateDelegate = this.update.createDelegate(this);
13003     /**
13004      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13005      * @type Function
13006      */
13007     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13008     /**
13009      * @private
13010      */
13011     this.successDelegate = this.processSuccess.createDelegate(this);
13012     /**
13013      * @private
13014      */
13015     this.failureDelegate = this.processFailure.createDelegate(this);
13016
13017     if(!this.renderer){
13018      /**
13019       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13020       */
13021     this.renderer = new Roo.UpdateManager.BasicRenderer();
13022     }
13023     
13024     Roo.UpdateManager.superclass.constructor.call(this);
13025 };
13026
13027 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13028     /**
13029      * Get the Element this UpdateManager is bound to
13030      * @return {Roo.Element} The element
13031      */
13032     getEl : function(){
13033         return this.el;
13034     },
13035     /**
13036      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13037      * @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:
13038 <pre><code>
13039 um.update({<br/>
13040     url: "your-url.php",<br/>
13041     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13042     callback: yourFunction,<br/>
13043     scope: yourObject, //(optional scope)  <br/>
13044     discardUrl: false, <br/>
13045     nocache: false,<br/>
13046     text: "Loading...",<br/>
13047     timeout: 30,<br/>
13048     scripts: false<br/>
13049 });
13050 </code></pre>
13051      * The only required property is url. The optional properties nocache, text and scripts
13052      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13053      * @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}
13054      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13055      * @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.
13056      */
13057     update : function(url, params, callback, discardUrl){
13058         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13059             var method = this.method,
13060                 cfg;
13061             if(typeof url == "object"){ // must be config object
13062                 cfg = url;
13063                 url = cfg.url;
13064                 params = params || cfg.params;
13065                 callback = callback || cfg.callback;
13066                 discardUrl = discardUrl || cfg.discardUrl;
13067                 if(callback && cfg.scope){
13068                     callback = callback.createDelegate(cfg.scope);
13069                 }
13070                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13071                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13072                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13073                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13074                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13075             }
13076             this.showLoading();
13077             if(!discardUrl){
13078                 this.defaultUrl = url;
13079             }
13080             if(typeof url == "function"){
13081                 url = url.call(this);
13082             }
13083
13084             method = method || (params ? "POST" : "GET");
13085             if(method == "GET"){
13086                 url = this.prepareUrl(url);
13087             }
13088
13089             var o = Roo.apply(cfg ||{}, {
13090                 url : url,
13091                 params: params,
13092                 success: this.successDelegate,
13093                 failure: this.failureDelegate,
13094                 callback: undefined,
13095                 timeout: (this.timeout*1000),
13096                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13097             });
13098             Roo.log("updated manager called with timeout of " + o.timeout);
13099             this.transaction = Roo.Ajax.request(o);
13100         }
13101     },
13102
13103     /**
13104      * 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.
13105      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13106      * @param {String/HTMLElement} form The form Id or form element
13107      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13108      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13109      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13110      */
13111     formUpdate : function(form, url, reset, callback){
13112         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13113             if(typeof url == "function"){
13114                 url = url.call(this);
13115             }
13116             form = Roo.getDom(form);
13117             this.transaction = Roo.Ajax.request({
13118                 form: form,
13119                 url:url,
13120                 success: this.successDelegate,
13121                 failure: this.failureDelegate,
13122                 timeout: (this.timeout*1000),
13123                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13124             });
13125             this.showLoading.defer(1, this);
13126         }
13127     },
13128
13129     /**
13130      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13131      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13132      */
13133     refresh : function(callback){
13134         if(this.defaultUrl == null){
13135             return;
13136         }
13137         this.update(this.defaultUrl, null, callback, true);
13138     },
13139
13140     /**
13141      * Set this element to auto refresh.
13142      * @param {Number} interval How often to update (in seconds).
13143      * @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)
13144      * @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}
13145      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13146      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13147      */
13148     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13149         if(refreshNow){
13150             this.update(url || this.defaultUrl, params, callback, true);
13151         }
13152         if(this.autoRefreshProcId){
13153             clearInterval(this.autoRefreshProcId);
13154         }
13155         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13156     },
13157
13158     /**
13159      * Stop auto refresh on this element.
13160      */
13161      stopAutoRefresh : function(){
13162         if(this.autoRefreshProcId){
13163             clearInterval(this.autoRefreshProcId);
13164             delete this.autoRefreshProcId;
13165         }
13166     },
13167
13168     isAutoRefreshing : function(){
13169        return this.autoRefreshProcId ? true : false;
13170     },
13171     /**
13172      * Called to update the element to "Loading" state. Override to perform custom action.
13173      */
13174     showLoading : function(){
13175         if(this.showLoadIndicator){
13176             this.el.update(this.indicatorText);
13177         }
13178     },
13179
13180     /**
13181      * Adds unique parameter to query string if disableCaching = true
13182      * @private
13183      */
13184     prepareUrl : function(url){
13185         if(this.disableCaching){
13186             var append = "_dc=" + (new Date().getTime());
13187             if(url.indexOf("?") !== -1){
13188                 url += "&" + append;
13189             }else{
13190                 url += "?" + append;
13191             }
13192         }
13193         return url;
13194     },
13195
13196     /**
13197      * @private
13198      */
13199     processSuccess : function(response){
13200         this.transaction = null;
13201         if(response.argument.form && response.argument.reset){
13202             try{ // put in try/catch since some older FF releases had problems with this
13203                 response.argument.form.reset();
13204             }catch(e){}
13205         }
13206         if(this.loadScripts){
13207             this.renderer.render(this.el, response, this,
13208                 this.updateComplete.createDelegate(this, [response]));
13209         }else{
13210             this.renderer.render(this.el, response, this);
13211             this.updateComplete(response);
13212         }
13213     },
13214
13215     updateComplete : function(response){
13216         this.fireEvent("update", this.el, response);
13217         if(typeof response.argument.callback == "function"){
13218             response.argument.callback(this.el, true, response);
13219         }
13220     },
13221
13222     /**
13223      * @private
13224      */
13225     processFailure : function(response){
13226         this.transaction = null;
13227         this.fireEvent("failure", this.el, response);
13228         if(typeof response.argument.callback == "function"){
13229             response.argument.callback(this.el, false, response);
13230         }
13231     },
13232
13233     /**
13234      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13235      * @param {Object} renderer The object implementing the render() method
13236      */
13237     setRenderer : function(renderer){
13238         this.renderer = renderer;
13239     },
13240
13241     getRenderer : function(){
13242        return this.renderer;
13243     },
13244
13245     /**
13246      * Set the defaultUrl used for updates
13247      * @param {String/Function} defaultUrl The url or a function to call to get the url
13248      */
13249     setDefaultUrl : function(defaultUrl){
13250         this.defaultUrl = defaultUrl;
13251     },
13252
13253     /**
13254      * Aborts the executing transaction
13255      */
13256     abort : function(){
13257         if(this.transaction){
13258             Roo.Ajax.abort(this.transaction);
13259         }
13260     },
13261
13262     /**
13263      * Returns true if an update is in progress
13264      * @return {Boolean}
13265      */
13266     isUpdating : function(){
13267         if(this.transaction){
13268             return Roo.Ajax.isLoading(this.transaction);
13269         }
13270         return false;
13271     }
13272 });
13273
13274 /**
13275  * @class Roo.UpdateManager.defaults
13276  * @static (not really - but it helps the doc tool)
13277  * The defaults collection enables customizing the default properties of UpdateManager
13278  */
13279    Roo.UpdateManager.defaults = {
13280        /**
13281          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13282          * @type Number
13283          */
13284          timeout : 30,
13285
13286          /**
13287          * True to process scripts by default (Defaults to false).
13288          * @type Boolean
13289          */
13290         loadScripts : false,
13291
13292         /**
13293         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13294         * @type String
13295         */
13296         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13297         /**
13298          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13299          * @type Boolean
13300          */
13301         disableCaching : false,
13302         /**
13303          * Whether to show indicatorText when loading (Defaults to true).
13304          * @type Boolean
13305          */
13306         showLoadIndicator : true,
13307         /**
13308          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13309          * @type String
13310          */
13311         indicatorText : '<div class="loading-indicator">Loading...</div>'
13312    };
13313
13314 /**
13315  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13316  *Usage:
13317  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13318  * @param {String/HTMLElement/Roo.Element} el The element to update
13319  * @param {String} url The url
13320  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13321  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13322  * @static
13323  * @deprecated
13324  * @member Roo.UpdateManager
13325  */
13326 Roo.UpdateManager.updateElement = function(el, url, params, options){
13327     var um = Roo.get(el, true).getUpdateManager();
13328     Roo.apply(um, options);
13329     um.update(url, params, options ? options.callback : null);
13330 };
13331 // alias for backwards compat
13332 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13333 /**
13334  * @class Roo.UpdateManager.BasicRenderer
13335  * Default Content renderer. Updates the elements innerHTML with the responseText.
13336  */
13337 Roo.UpdateManager.BasicRenderer = function(){};
13338
13339 Roo.UpdateManager.BasicRenderer.prototype = {
13340     /**
13341      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13342      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13343      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13344      * @param {Roo.Element} el The element being rendered
13345      * @param {Object} response The YUI Connect response object
13346      * @param {UpdateManager} updateManager The calling update manager
13347      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13348      */
13349      render : function(el, response, updateManager, callback){
13350         el.update(response.responseText, updateManager.loadScripts, callback);
13351     }
13352 };
13353 /*
13354  * Based on:
13355  * Roo JS
13356  * (c)) Alan Knowles
13357  * Licence : LGPL
13358  */
13359
13360
13361 /**
13362  * @class Roo.DomTemplate
13363  * @extends Roo.Template
13364  * An effort at a dom based template engine..
13365  *
13366  * Similar to XTemplate, except it uses dom parsing to create the template..
13367  *
13368  * Supported features:
13369  *
13370  *  Tags:
13371
13372 <pre><code>
13373       {a_variable} - output encoded.
13374       {a_variable.format:("Y-m-d")} - call a method on the variable
13375       {a_variable:raw} - unencoded output
13376       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13377       {a_variable:this.method_on_template(...)} - call a method on the template object.
13378  
13379 </code></pre>
13380  *  The tpl tag:
13381 <pre><code>
13382         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13383         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13384         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13385         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13386   
13387 </code></pre>
13388  *      
13389  */
13390 Roo.DomTemplate = function()
13391 {
13392      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13393      if (this.html) {
13394         this.compile();
13395      }
13396 };
13397
13398
13399 Roo.extend(Roo.DomTemplate, Roo.Template, {
13400     /**
13401      * id counter for sub templates.
13402      */
13403     id : 0,
13404     /**
13405      * flag to indicate if dom parser is inside a pre,
13406      * it will strip whitespace if not.
13407      */
13408     inPre : false,
13409     
13410     /**
13411      * The various sub templates
13412      */
13413     tpls : false,
13414     
13415     
13416     
13417     /**
13418      *
13419      * basic tag replacing syntax
13420      * WORD:WORD()
13421      *
13422      * // you can fake an object call by doing this
13423      *  x.t:(test,tesT) 
13424      * 
13425      */
13426     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13427     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13428     
13429     iterChild : function (node, method) {
13430         
13431         var oldPre = this.inPre;
13432         if (node.tagName == 'PRE') {
13433             this.inPre = true;
13434         }
13435         for( var i = 0; i < node.childNodes.length; i++) {
13436             method.call(this, node.childNodes[i]);
13437         }
13438         this.inPre = oldPre;
13439     },
13440     
13441     
13442     
13443     /**
13444      * compile the template
13445      *
13446      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13447      *
13448      */
13449     compile: function()
13450     {
13451         var s = this.html;
13452         
13453         // covert the html into DOM...
13454         var doc = false;
13455         var div =false;
13456         try {
13457             doc = document.implementation.createHTMLDocument("");
13458             doc.documentElement.innerHTML =   this.html  ;
13459             div = doc.documentElement;
13460         } catch (e) {
13461             // old IE... - nasty -- it causes all sorts of issues.. with
13462             // images getting pulled from server..
13463             div = document.createElement('div');
13464             div.innerHTML = this.html;
13465         }
13466         //doc.documentElement.innerHTML = htmlBody
13467          
13468         
13469         
13470         this.tpls = [];
13471         var _t = this;
13472         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13473         
13474         var tpls = this.tpls;
13475         
13476         // create a top level template from the snippet..
13477         
13478         //Roo.log(div.innerHTML);
13479         
13480         var tpl = {
13481             uid : 'master',
13482             id : this.id++,
13483             attr : false,
13484             value : false,
13485             body : div.innerHTML,
13486             
13487             forCall : false,
13488             execCall : false,
13489             dom : div,
13490             isTop : true
13491             
13492         };
13493         tpls.unshift(tpl);
13494         
13495         
13496         // compile them...
13497         this.tpls = [];
13498         Roo.each(tpls, function(tp){
13499             this.compileTpl(tp);
13500             this.tpls[tp.id] = tp;
13501         }, this);
13502         
13503         this.master = tpls[0];
13504         return this;
13505         
13506         
13507     },
13508     
13509     compileNode : function(node, istop) {
13510         // test for
13511         //Roo.log(node);
13512         
13513         
13514         // skip anything not a tag..
13515         if (node.nodeType != 1) {
13516             if (node.nodeType == 3 && !this.inPre) {
13517                 // reduce white space..
13518                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13519                 
13520             }
13521             return;
13522         }
13523         
13524         var tpl = {
13525             uid : false,
13526             id : false,
13527             attr : false,
13528             value : false,
13529             body : '',
13530             
13531             forCall : false,
13532             execCall : false,
13533             dom : false,
13534             isTop : istop
13535             
13536             
13537         };
13538         
13539         
13540         switch(true) {
13541             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13542             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13543             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13544             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13545             // no default..
13546         }
13547         
13548         
13549         if (!tpl.attr) {
13550             // just itterate children..
13551             this.iterChild(node,this.compileNode);
13552             return;
13553         }
13554         tpl.uid = this.id++;
13555         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13556         node.removeAttribute('roo-'+ tpl.attr);
13557         if (tpl.attr != 'name') {
13558             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13559             node.parentNode.replaceChild(placeholder,  node);
13560         } else {
13561             
13562             var placeholder =  document.createElement('span');
13563             placeholder.className = 'roo-tpl-' + tpl.value;
13564             node.parentNode.replaceChild(placeholder,  node);
13565         }
13566         
13567         // parent now sees '{domtplXXXX}
13568         this.iterChild(node,this.compileNode);
13569         
13570         // we should now have node body...
13571         var div = document.createElement('div');
13572         div.appendChild(node);
13573         tpl.dom = node;
13574         // this has the unfortunate side effect of converting tagged attributes
13575         // eg. href="{...}" into %7C...%7D
13576         // this has been fixed by searching for those combo's although it's a bit hacky..
13577         
13578         
13579         tpl.body = div.innerHTML;
13580         
13581         
13582          
13583         tpl.id = tpl.uid;
13584         switch(tpl.attr) {
13585             case 'for' :
13586                 switch (tpl.value) {
13587                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13588                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13589                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13590                 }
13591                 break;
13592             
13593             case 'exec':
13594                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13595                 break;
13596             
13597             case 'if':     
13598                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13599                 break;
13600             
13601             case 'name':
13602                 tpl.id  = tpl.value; // replace non characters???
13603                 break;
13604             
13605         }
13606         
13607         
13608         this.tpls.push(tpl);
13609         
13610         
13611         
13612     },
13613     
13614     
13615     
13616     
13617     /**
13618      * Compile a segment of the template into a 'sub-template'
13619      *
13620      * 
13621      * 
13622      *
13623      */
13624     compileTpl : function(tpl)
13625     {
13626         var fm = Roo.util.Format;
13627         var useF = this.disableFormats !== true;
13628         
13629         var sep = Roo.isGecko ? "+\n" : ",\n";
13630         
13631         var undef = function(str) {
13632             Roo.debug && Roo.log("Property not found :"  + str);
13633             return '';
13634         };
13635           
13636         //Roo.log(tpl.body);
13637         
13638         
13639         
13640         var fn = function(m, lbrace, name, format, args)
13641         {
13642             //Roo.log("ARGS");
13643             //Roo.log(arguments);
13644             args = args ? args.replace(/\\'/g,"'") : args;
13645             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13646             if (typeof(format) == 'undefined') {
13647                 format =  'htmlEncode'; 
13648             }
13649             if (format == 'raw' ) {
13650                 format = false;
13651             }
13652             
13653             if(name.substr(0, 6) == 'domtpl'){
13654                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13655             }
13656             
13657             // build an array of options to determine if value is undefined..
13658             
13659             // basically get 'xxxx.yyyy' then do
13660             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13661             //    (function () { Roo.log("Property not found"); return ''; })() :
13662             //    ......
13663             
13664             var udef_ar = [];
13665             var lookfor = '';
13666             Roo.each(name.split('.'), function(st) {
13667                 lookfor += (lookfor.length ? '.': '') + st;
13668                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13669             });
13670             
13671             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13672             
13673             
13674             if(format && useF){
13675                 
13676                 args = args ? ',' + args : "";
13677                  
13678                 if(format.substr(0, 5) != "this."){
13679                     format = "fm." + format + '(';
13680                 }else{
13681                     format = 'this.call("'+ format.substr(5) + '", ';
13682                     args = ", values";
13683                 }
13684                 
13685                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13686             }
13687              
13688             if (args && args.length) {
13689                 // called with xxyx.yuu:(test,test)
13690                 // change to ()
13691                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13692             }
13693             // raw.. - :raw modifier..
13694             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13695             
13696         };
13697         var body;
13698         // branched to use + in gecko and [].join() in others
13699         if(Roo.isGecko){
13700             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13701                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13702                     "';};};";
13703         }else{
13704             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13705             body.push(tpl.body.replace(/(\r\n|\n)/g,
13706                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13707             body.push("'].join('');};};");
13708             body = body.join('');
13709         }
13710         
13711         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13712        
13713         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13714         eval(body);
13715         
13716         return this;
13717     },
13718      
13719     /**
13720      * same as applyTemplate, except it's done to one of the subTemplates
13721      * when using named templates, you can do:
13722      *
13723      * var str = pl.applySubTemplate('your-name', values);
13724      *
13725      * 
13726      * @param {Number} id of the template
13727      * @param {Object} values to apply to template
13728      * @param {Object} parent (normaly the instance of this object)
13729      */
13730     applySubTemplate : function(id, values, parent)
13731     {
13732         
13733         
13734         var t = this.tpls[id];
13735         
13736         
13737         try { 
13738             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13739                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13740                 return '';
13741             }
13742         } catch(e) {
13743             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13744             Roo.log(values);
13745           
13746             return '';
13747         }
13748         try { 
13749             
13750             if(t.execCall && t.execCall.call(this, values, parent)){
13751                 return '';
13752             }
13753         } catch(e) {
13754             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13755             Roo.log(values);
13756             return '';
13757         }
13758         
13759         try {
13760             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13761             parent = t.target ? values : parent;
13762             if(t.forCall && vs instanceof Array){
13763                 var buf = [];
13764                 for(var i = 0, len = vs.length; i < len; i++){
13765                     try {
13766                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13767                     } catch (e) {
13768                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13769                         Roo.log(e.body);
13770                         //Roo.log(t.compiled);
13771                         Roo.log(vs[i]);
13772                     }   
13773                 }
13774                 return buf.join('');
13775             }
13776         } catch (e) {
13777             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13778             Roo.log(values);
13779             return '';
13780         }
13781         try {
13782             return t.compiled.call(this, vs, parent);
13783         } catch (e) {
13784             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13785             Roo.log(e.body);
13786             //Roo.log(t.compiled);
13787             Roo.log(values);
13788             return '';
13789         }
13790     },
13791
13792    
13793
13794     applyTemplate : function(values){
13795         return this.master.compiled.call(this, values, {});
13796         //var s = this.subs;
13797     },
13798
13799     apply : function(){
13800         return this.applyTemplate.apply(this, arguments);
13801     }
13802
13803  });
13804
13805 Roo.DomTemplate.from = function(el){
13806     el = Roo.getDom(el);
13807     return new Roo.Domtemplate(el.value || el.innerHTML);
13808 };/*
13809  * Based on:
13810  * Ext JS Library 1.1.1
13811  * Copyright(c) 2006-2007, Ext JS, LLC.
13812  *
13813  * Originally Released Under LGPL - original licence link has changed is not relivant.
13814  *
13815  * Fork - LGPL
13816  * <script type="text/javascript">
13817  */
13818
13819 /**
13820  * @class Roo.util.DelayedTask
13821  * Provides a convenient method of performing setTimeout where a new
13822  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13823  * You can use this class to buffer
13824  * the keypress events for a certain number of milliseconds, and perform only if they stop
13825  * for that amount of time.
13826  * @constructor The parameters to this constructor serve as defaults and are not required.
13827  * @param {Function} fn (optional) The default function to timeout
13828  * @param {Object} scope (optional) The default scope of that timeout
13829  * @param {Array} args (optional) The default Array of arguments
13830  */
13831 Roo.util.DelayedTask = function(fn, scope, args){
13832     var id = null, d, t;
13833
13834     var call = function(){
13835         var now = new Date().getTime();
13836         if(now - t >= d){
13837             clearInterval(id);
13838             id = null;
13839             fn.apply(scope, args || []);
13840         }
13841     };
13842     /**
13843      * Cancels any pending timeout and queues a new one
13844      * @param {Number} delay The milliseconds to delay
13845      * @param {Function} newFn (optional) Overrides function passed to constructor
13846      * @param {Object} newScope (optional) Overrides scope passed to constructor
13847      * @param {Array} newArgs (optional) Overrides args passed to constructor
13848      */
13849     this.delay = function(delay, newFn, newScope, newArgs){
13850         if(id && delay != d){
13851             this.cancel();
13852         }
13853         d = delay;
13854         t = new Date().getTime();
13855         fn = newFn || fn;
13856         scope = newScope || scope;
13857         args = newArgs || args;
13858         if(!id){
13859             id = setInterval(call, d);
13860         }
13861     };
13862
13863     /**
13864      * Cancel the last queued timeout
13865      */
13866     this.cancel = function(){
13867         if(id){
13868             clearInterval(id);
13869             id = null;
13870         }
13871     };
13872 };/*
13873  * Based on:
13874  * Ext JS Library 1.1.1
13875  * Copyright(c) 2006-2007, Ext JS, LLC.
13876  *
13877  * Originally Released Under LGPL - original licence link has changed is not relivant.
13878  *
13879  * Fork - LGPL
13880  * <script type="text/javascript">
13881  */
13882 /**
13883  * @class Roo.util.TaskRunner
13884  * Manage background tasks - not sure why this is better that setInterval?
13885  * @static
13886  *
13887  */
13888  
13889 Roo.util.TaskRunner = function(interval){
13890     interval = interval || 10;
13891     var tasks = [], removeQueue = [];
13892     var id = 0;
13893     var running = false;
13894
13895     var stopThread = function(){
13896         running = false;
13897         clearInterval(id);
13898         id = 0;
13899     };
13900
13901     var startThread = function(){
13902         if(!running){
13903             running = true;
13904             id = setInterval(runTasks, interval);
13905         }
13906     };
13907
13908     var removeTask = function(task){
13909         removeQueue.push(task);
13910         if(task.onStop){
13911             task.onStop();
13912         }
13913     };
13914
13915     var runTasks = function(){
13916         if(removeQueue.length > 0){
13917             for(var i = 0, len = removeQueue.length; i < len; i++){
13918                 tasks.remove(removeQueue[i]);
13919             }
13920             removeQueue = [];
13921             if(tasks.length < 1){
13922                 stopThread();
13923                 return;
13924             }
13925         }
13926         var now = new Date().getTime();
13927         for(var i = 0, len = tasks.length; i < len; ++i){
13928             var t = tasks[i];
13929             var itime = now - t.taskRunTime;
13930             if(t.interval <= itime){
13931                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13932                 t.taskRunTime = now;
13933                 if(rt === false || t.taskRunCount === t.repeat){
13934                     removeTask(t);
13935                     return;
13936                 }
13937             }
13938             if(t.duration && t.duration <= (now - t.taskStartTime)){
13939                 removeTask(t);
13940             }
13941         }
13942     };
13943
13944     /**
13945      * Queues a new task.
13946      * @param {Object} task
13947      *
13948      * Task property : interval = how frequent to run.
13949      * Task object should implement
13950      * function run()
13951      * Task object may implement
13952      * function onStop()
13953      */
13954     this.start = function(task){
13955         tasks.push(task);
13956         task.taskStartTime = new Date().getTime();
13957         task.taskRunTime = 0;
13958         task.taskRunCount = 0;
13959         startThread();
13960         return task;
13961     };
13962     /**
13963      * Stop  new task.
13964      * @param {Object} task
13965      */
13966     this.stop = function(task){
13967         removeTask(task);
13968         return task;
13969     };
13970     /**
13971      * Stop all Tasks
13972      */
13973     this.stopAll = function(){
13974         stopThread();
13975         for(var i = 0, len = tasks.length; i < len; i++){
13976             if(tasks[i].onStop){
13977                 tasks[i].onStop();
13978             }
13979         }
13980         tasks = [];
13981         removeQueue = [];
13982     };
13983 };
13984
13985 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13986  * Based on:
13987  * Ext JS Library 1.1.1
13988  * Copyright(c) 2006-2007, Ext JS, LLC.
13989  *
13990  * Originally Released Under LGPL - original licence link has changed is not relivant.
13991  *
13992  * Fork - LGPL
13993  * <script type="text/javascript">
13994  */
13995
13996  
13997 /**
13998  * @class Roo.util.MixedCollection
13999  * @extends Roo.util.Observable
14000  * A Collection class that maintains both numeric indexes and keys and exposes events.
14001  * @constructor
14002  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14003  * collection (defaults to false)
14004  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14005  * and return the key value for that item.  This is used when available to look up the key on items that
14006  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14007  * equivalent to providing an implementation for the {@link #getKey} method.
14008  */
14009 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14010     this.items = [];
14011     this.map = {};
14012     this.keys = [];
14013     this.length = 0;
14014     this.addEvents({
14015         /**
14016          * @event clear
14017          * Fires when the collection is cleared.
14018          */
14019         "clear" : true,
14020         /**
14021          * @event add
14022          * Fires when an item is added to the collection.
14023          * @param {Number} index The index at which the item was added.
14024          * @param {Object} o The item added.
14025          * @param {String} key The key associated with the added item.
14026          */
14027         "add" : true,
14028         /**
14029          * @event replace
14030          * Fires when an item is replaced in the collection.
14031          * @param {String} key he key associated with the new added.
14032          * @param {Object} old The item being replaced.
14033          * @param {Object} new The new item.
14034          */
14035         "replace" : true,
14036         /**
14037          * @event remove
14038          * Fires when an item is removed from the collection.
14039          * @param {Object} o The item being removed.
14040          * @param {String} key (optional) The key associated with the removed item.
14041          */
14042         "remove" : true,
14043         "sort" : true
14044     });
14045     this.allowFunctions = allowFunctions === true;
14046     if(keyFn){
14047         this.getKey = keyFn;
14048     }
14049     Roo.util.MixedCollection.superclass.constructor.call(this);
14050 };
14051
14052 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14053     allowFunctions : false,
14054     
14055 /**
14056  * Adds an item to the collection.
14057  * @param {String} key The key to associate with the item
14058  * @param {Object} o The item to add.
14059  * @return {Object} The item added.
14060  */
14061     add : function(key, o){
14062         if(arguments.length == 1){
14063             o = arguments[0];
14064             key = this.getKey(o);
14065         }
14066         if(typeof key == "undefined" || key === null){
14067             this.length++;
14068             this.items.push(o);
14069             this.keys.push(null);
14070         }else{
14071             var old = this.map[key];
14072             if(old){
14073                 return this.replace(key, o);
14074             }
14075             this.length++;
14076             this.items.push(o);
14077             this.map[key] = o;
14078             this.keys.push(key);
14079         }
14080         this.fireEvent("add", this.length-1, o, key);
14081         return o;
14082     },
14083        
14084 /**
14085   * MixedCollection has a generic way to fetch keys if you implement getKey.
14086 <pre><code>
14087 // normal way
14088 var mc = new Roo.util.MixedCollection();
14089 mc.add(someEl.dom.id, someEl);
14090 mc.add(otherEl.dom.id, otherEl);
14091 //and so on
14092
14093 // using getKey
14094 var mc = new Roo.util.MixedCollection();
14095 mc.getKey = function(el){
14096    return el.dom.id;
14097 };
14098 mc.add(someEl);
14099 mc.add(otherEl);
14100
14101 // or via the constructor
14102 var mc = new Roo.util.MixedCollection(false, function(el){
14103    return el.dom.id;
14104 });
14105 mc.add(someEl);
14106 mc.add(otherEl);
14107 </code></pre>
14108  * @param o {Object} The item for which to find the key.
14109  * @return {Object} The key for the passed item.
14110  */
14111     getKey : function(o){
14112          return o.id; 
14113     },
14114    
14115 /**
14116  * Replaces an item in the collection.
14117  * @param {String} key The key associated with the item to replace, or the item to replace.
14118  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14119  * @return {Object}  The new item.
14120  */
14121     replace : function(key, o){
14122         if(arguments.length == 1){
14123             o = arguments[0];
14124             key = this.getKey(o);
14125         }
14126         var old = this.item(key);
14127         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14128              return this.add(key, o);
14129         }
14130         var index = this.indexOfKey(key);
14131         this.items[index] = o;
14132         this.map[key] = o;
14133         this.fireEvent("replace", key, old, o);
14134         return o;
14135     },
14136    
14137 /**
14138  * Adds all elements of an Array or an Object to the collection.
14139  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14140  * an Array of values, each of which are added to the collection.
14141  */
14142     addAll : function(objs){
14143         if(arguments.length > 1 || objs instanceof Array){
14144             var args = arguments.length > 1 ? arguments : objs;
14145             for(var i = 0, len = args.length; i < len; i++){
14146                 this.add(args[i]);
14147             }
14148         }else{
14149             for(var key in objs){
14150                 if(this.allowFunctions || typeof objs[key] != "function"){
14151                     this.add(key, objs[key]);
14152                 }
14153             }
14154         }
14155     },
14156    
14157 /**
14158  * Executes the specified function once for every item in the collection, passing each
14159  * item as the first and only parameter. returning false from the function will stop the iteration.
14160  * @param {Function} fn The function to execute for each item.
14161  * @param {Object} scope (optional) The scope in which to execute the function.
14162  */
14163     each : function(fn, scope){
14164         var items = [].concat(this.items); // each safe for removal
14165         for(var i = 0, len = items.length; i < len; i++){
14166             if(fn.call(scope || items[i], items[i], i, len) === false){
14167                 break;
14168             }
14169         }
14170     },
14171    
14172 /**
14173  * Executes the specified function once for every key in the collection, passing each
14174  * key, and its associated item as the first two parameters.
14175  * @param {Function} fn The function to execute for each item.
14176  * @param {Object} scope (optional) The scope in which to execute the function.
14177  */
14178     eachKey : function(fn, scope){
14179         for(var i = 0, len = this.keys.length; i < len; i++){
14180             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14181         }
14182     },
14183    
14184 /**
14185  * Returns the first item in the collection which elicits a true return value from the
14186  * passed selection function.
14187  * @param {Function} fn The selection function to execute for each item.
14188  * @param {Object} scope (optional) The scope in which to execute the function.
14189  * @return {Object} The first item in the collection which returned true from the selection function.
14190  */
14191     find : function(fn, scope){
14192         for(var i = 0, len = this.items.length; i < len; i++){
14193             if(fn.call(scope || window, this.items[i], this.keys[i])){
14194                 return this.items[i];
14195             }
14196         }
14197         return null;
14198     },
14199    
14200 /**
14201  * Inserts an item at the specified index in the collection.
14202  * @param {Number} index The index to insert the item at.
14203  * @param {String} key The key to associate with the new item, or the item itself.
14204  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14205  * @return {Object} The item inserted.
14206  */
14207     insert : function(index, key, o){
14208         if(arguments.length == 2){
14209             o = arguments[1];
14210             key = this.getKey(o);
14211         }
14212         if(index >= this.length){
14213             return this.add(key, o);
14214         }
14215         this.length++;
14216         this.items.splice(index, 0, o);
14217         if(typeof key != "undefined" && key != null){
14218             this.map[key] = o;
14219         }
14220         this.keys.splice(index, 0, key);
14221         this.fireEvent("add", index, o, key);
14222         return o;
14223     },
14224    
14225 /**
14226  * Removed an item from the collection.
14227  * @param {Object} o The item to remove.
14228  * @return {Object} The item removed.
14229  */
14230     remove : function(o){
14231         return this.removeAt(this.indexOf(o));
14232     },
14233    
14234 /**
14235  * Remove an item from a specified index in the collection.
14236  * @param {Number} index The index within the collection of the item to remove.
14237  */
14238     removeAt : function(index){
14239         if(index < this.length && index >= 0){
14240             this.length--;
14241             var o = this.items[index];
14242             this.items.splice(index, 1);
14243             var key = this.keys[index];
14244             if(typeof key != "undefined"){
14245                 delete this.map[key];
14246             }
14247             this.keys.splice(index, 1);
14248             this.fireEvent("remove", o, key);
14249         }
14250     },
14251    
14252 /**
14253  * Removed an item associated with the passed key fom the collection.
14254  * @param {String} key The key of the item to remove.
14255  */
14256     removeKey : function(key){
14257         return this.removeAt(this.indexOfKey(key));
14258     },
14259    
14260 /**
14261  * Returns the number of items in the collection.
14262  * @return {Number} the number of items in the collection.
14263  */
14264     getCount : function(){
14265         return this.length; 
14266     },
14267    
14268 /**
14269  * Returns index within the collection of the passed Object.
14270  * @param {Object} o The item to find the index of.
14271  * @return {Number} index of the item.
14272  */
14273     indexOf : function(o){
14274         if(!this.items.indexOf){
14275             for(var i = 0, len = this.items.length; i < len; i++){
14276                 if(this.items[i] == o) {
14277                     return i;
14278                 }
14279             }
14280             return -1;
14281         }else{
14282             return this.items.indexOf(o);
14283         }
14284     },
14285    
14286 /**
14287  * Returns index within the collection of the passed key.
14288  * @param {String} key The key to find the index of.
14289  * @return {Number} index of the key.
14290  */
14291     indexOfKey : function(key){
14292         if(!this.keys.indexOf){
14293             for(var i = 0, len = this.keys.length; i < len; i++){
14294                 if(this.keys[i] == key) {
14295                     return i;
14296                 }
14297             }
14298             return -1;
14299         }else{
14300             return this.keys.indexOf(key);
14301         }
14302     },
14303    
14304 /**
14305  * Returns the item associated with the passed key OR index. Key has priority over index.
14306  * @param {String/Number} key The key or index of the item.
14307  * @return {Object} The item associated with the passed key.
14308  */
14309     item : function(key){
14310         if (key === 'length') {
14311             return null;
14312         }
14313         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14314         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14315     },
14316     
14317 /**
14318  * Returns the item at the specified index.
14319  * @param {Number} index The index of the item.
14320  * @return {Object}
14321  */
14322     itemAt : function(index){
14323         return this.items[index];
14324     },
14325     
14326 /**
14327  * Returns the item associated with the passed key.
14328  * @param {String/Number} key The key of the item.
14329  * @return {Object} The item associated with the passed key.
14330  */
14331     key : function(key){
14332         return this.map[key];
14333     },
14334    
14335 /**
14336  * Returns true if the collection contains the passed Object as an item.
14337  * @param {Object} o  The Object to look for in the collection.
14338  * @return {Boolean} True if the collection contains the Object as an item.
14339  */
14340     contains : function(o){
14341         return this.indexOf(o) != -1;
14342     },
14343    
14344 /**
14345  * Returns true if the collection contains the passed Object as a key.
14346  * @param {String} key The key to look for in the collection.
14347  * @return {Boolean} True if the collection contains the Object as a key.
14348  */
14349     containsKey : function(key){
14350         return typeof this.map[key] != "undefined";
14351     },
14352    
14353 /**
14354  * Removes all items from the collection.
14355  */
14356     clear : function(){
14357         this.length = 0;
14358         this.items = [];
14359         this.keys = [];
14360         this.map = {};
14361         this.fireEvent("clear");
14362     },
14363    
14364 /**
14365  * Returns the first item in the collection.
14366  * @return {Object} the first item in the collection..
14367  */
14368     first : function(){
14369         return this.items[0]; 
14370     },
14371    
14372 /**
14373  * Returns the last item in the collection.
14374  * @return {Object} the last item in the collection..
14375  */
14376     last : function(){
14377         return this.items[this.length-1];   
14378     },
14379     
14380     _sort : function(property, dir, fn){
14381         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14382         fn = fn || function(a, b){
14383             return a-b;
14384         };
14385         var c = [], k = this.keys, items = this.items;
14386         for(var i = 0, len = items.length; i < len; i++){
14387             c[c.length] = {key: k[i], value: items[i], index: i};
14388         }
14389         c.sort(function(a, b){
14390             var v = fn(a[property], b[property]) * dsc;
14391             if(v == 0){
14392                 v = (a.index < b.index ? -1 : 1);
14393             }
14394             return v;
14395         });
14396         for(var i = 0, len = c.length; i < len; i++){
14397             items[i] = c[i].value;
14398             k[i] = c[i].key;
14399         }
14400         this.fireEvent("sort", this);
14401     },
14402     
14403     /**
14404      * Sorts this collection with the passed comparison function
14405      * @param {String} direction (optional) "ASC" or "DESC"
14406      * @param {Function} fn (optional) comparison function
14407      */
14408     sort : function(dir, fn){
14409         this._sort("value", dir, fn);
14410     },
14411     
14412     /**
14413      * Sorts this collection by keys
14414      * @param {String} direction (optional) "ASC" or "DESC"
14415      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14416      */
14417     keySort : function(dir, fn){
14418         this._sort("key", dir, fn || function(a, b){
14419             return String(a).toUpperCase()-String(b).toUpperCase();
14420         });
14421     },
14422     
14423     /**
14424      * Returns a range of items in this collection
14425      * @param {Number} startIndex (optional) defaults to 0
14426      * @param {Number} endIndex (optional) default to the last item
14427      * @return {Array} An array of items
14428      */
14429     getRange : function(start, end){
14430         var items = this.items;
14431         if(items.length < 1){
14432             return [];
14433         }
14434         start = start || 0;
14435         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14436         var r = [];
14437         if(start <= end){
14438             for(var i = start; i <= end; i++) {
14439                     r[r.length] = items[i];
14440             }
14441         }else{
14442             for(var i = start; i >= end; i--) {
14443                     r[r.length] = items[i];
14444             }
14445         }
14446         return r;
14447     },
14448         
14449     /**
14450      * Filter the <i>objects</i> in this collection by a specific property. 
14451      * Returns a new collection that has been filtered.
14452      * @param {String} property A property on your objects
14453      * @param {String/RegExp} value Either string that the property values 
14454      * should start with or a RegExp to test against the property
14455      * @return {MixedCollection} The new filtered collection
14456      */
14457     filter : function(property, value){
14458         if(!value.exec){ // not a regex
14459             value = String(value);
14460             if(value.length == 0){
14461                 return this.clone();
14462             }
14463             value = new RegExp("^" + Roo.escapeRe(value), "i");
14464         }
14465         return this.filterBy(function(o){
14466             return o && value.test(o[property]);
14467         });
14468         },
14469     
14470     /**
14471      * Filter by a function. * Returns a new collection that has been filtered.
14472      * The passed function will be called with each 
14473      * object in the collection. If the function returns true, the value is included 
14474      * otherwise it is filtered.
14475      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14476      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14477      * @return {MixedCollection} The new filtered collection
14478      */
14479     filterBy : function(fn, scope){
14480         var r = new Roo.util.MixedCollection();
14481         r.getKey = this.getKey;
14482         var k = this.keys, it = this.items;
14483         for(var i = 0, len = it.length; i < len; i++){
14484             if(fn.call(scope||this, it[i], k[i])){
14485                                 r.add(k[i], it[i]);
14486                         }
14487         }
14488         return r;
14489     },
14490     
14491     /**
14492      * Creates a duplicate of this collection
14493      * @return {MixedCollection}
14494      */
14495     clone : function(){
14496         var r = new Roo.util.MixedCollection();
14497         var k = this.keys, it = this.items;
14498         for(var i = 0, len = it.length; i < len; i++){
14499             r.add(k[i], it[i]);
14500         }
14501         r.getKey = this.getKey;
14502         return r;
14503     }
14504 });
14505 /**
14506  * Returns the item associated with the passed key or index.
14507  * @method
14508  * @param {String/Number} key The key or index of the item.
14509  * @return {Object} The item associated with the passed key.
14510  */
14511 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14512  * Based on:
14513  * Ext JS Library 1.1.1
14514  * Copyright(c) 2006-2007, Ext JS, LLC.
14515  *
14516  * Originally Released Under LGPL - original licence link has changed is not relivant.
14517  *
14518  * Fork - LGPL
14519  * <script type="text/javascript">
14520  */
14521 /**
14522  * @class Roo.util.JSON
14523  * Modified version of Douglas Crockford"s json.js that doesn"t
14524  * mess with the Object prototype 
14525  * http://www.json.org/js.html
14526  * @static
14527  */
14528 Roo.util.JSON = new (function(){
14529     var useHasOwn = {}.hasOwnProperty ? true : false;
14530     
14531     // crashes Safari in some instances
14532     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14533     
14534     var pad = function(n) {
14535         return n < 10 ? "0" + n : n;
14536     };
14537     
14538     var m = {
14539         "\b": '\\b',
14540         "\t": '\\t',
14541         "\n": '\\n',
14542         "\f": '\\f',
14543         "\r": '\\r',
14544         '"' : '\\"',
14545         "\\": '\\\\'
14546     };
14547
14548     var encodeString = function(s){
14549         if (/["\\\x00-\x1f]/.test(s)) {
14550             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14551                 var c = m[b];
14552                 if(c){
14553                     return c;
14554                 }
14555                 c = b.charCodeAt();
14556                 return "\\u00" +
14557                     Math.floor(c / 16).toString(16) +
14558                     (c % 16).toString(16);
14559             }) + '"';
14560         }
14561         return '"' + s + '"';
14562     };
14563     
14564     var encodeArray = function(o){
14565         var a = ["["], b, i, l = o.length, v;
14566             for (i = 0; i < l; i += 1) {
14567                 v = o[i];
14568                 switch (typeof v) {
14569                     case "undefined":
14570                     case "function":
14571                     case "unknown":
14572                         break;
14573                     default:
14574                         if (b) {
14575                             a.push(',');
14576                         }
14577                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14578                         b = true;
14579                 }
14580             }
14581             a.push("]");
14582             return a.join("");
14583     };
14584     
14585     var encodeDate = function(o){
14586         return '"' + o.getFullYear() + "-" +
14587                 pad(o.getMonth() + 1) + "-" +
14588                 pad(o.getDate()) + "T" +
14589                 pad(o.getHours()) + ":" +
14590                 pad(o.getMinutes()) + ":" +
14591                 pad(o.getSeconds()) + '"';
14592     };
14593     
14594     /**
14595      * Encodes an Object, Array or other value
14596      * @param {Mixed} o The variable to encode
14597      * @return {String} The JSON string
14598      */
14599     this.encode = function(o)
14600     {
14601         // should this be extended to fully wrap stringify..
14602         
14603         if(typeof o == "undefined" || o === null){
14604             return "null";
14605         }else if(o instanceof Array){
14606             return encodeArray(o);
14607         }else if(o instanceof Date){
14608             return encodeDate(o);
14609         }else if(typeof o == "string"){
14610             return encodeString(o);
14611         }else if(typeof o == "number"){
14612             return isFinite(o) ? String(o) : "null";
14613         }else if(typeof o == "boolean"){
14614             return String(o);
14615         }else {
14616             var a = ["{"], b, i, v;
14617             for (i in o) {
14618                 if(!useHasOwn || o.hasOwnProperty(i)) {
14619                     v = o[i];
14620                     switch (typeof v) {
14621                     case "undefined":
14622                     case "function":
14623                     case "unknown":
14624                         break;
14625                     default:
14626                         if(b){
14627                             a.push(',');
14628                         }
14629                         a.push(this.encode(i), ":",
14630                                 v === null ? "null" : this.encode(v));
14631                         b = true;
14632                     }
14633                 }
14634             }
14635             a.push("}");
14636             return a.join("");
14637         }
14638     };
14639     
14640     /**
14641      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14642      * @param {String} json The JSON string
14643      * @return {Object} The resulting object
14644      */
14645     this.decode = function(json){
14646         
14647         return  /** eval:var:json */ eval("(" + json + ')');
14648     };
14649 })();
14650 /** 
14651  * Shorthand for {@link Roo.util.JSON#encode}
14652  * @member Roo encode 
14653  * @method */
14654 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14655 /** 
14656  * Shorthand for {@link Roo.util.JSON#decode}
14657  * @member Roo decode 
14658  * @method */
14659 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14660 /*
14661  * Based on:
14662  * Ext JS Library 1.1.1
14663  * Copyright(c) 2006-2007, Ext JS, LLC.
14664  *
14665  * Originally Released Under LGPL - original licence link has changed is not relivant.
14666  *
14667  * Fork - LGPL
14668  * <script type="text/javascript">
14669  */
14670  
14671 /**
14672  * @class Roo.util.Format
14673  * Reusable data formatting functions
14674  * @static
14675  */
14676 Roo.util.Format = function(){
14677     var trimRe = /^\s+|\s+$/g;
14678     return {
14679         /**
14680          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14681          * @param {String} value The string to truncate
14682          * @param {Number} length The maximum length to allow before truncating
14683          * @return {String} The converted text
14684          */
14685         ellipsis : function(value, len){
14686             if(value && value.length > len){
14687                 return value.substr(0, len-3)+"...";
14688             }
14689             return value;
14690         },
14691
14692         /**
14693          * Checks a reference and converts it to empty string if it is undefined
14694          * @param {Mixed} value Reference to check
14695          * @return {Mixed} Empty string if converted, otherwise the original value
14696          */
14697         undef : function(value){
14698             return typeof value != "undefined" ? value : "";
14699         },
14700
14701         /**
14702          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14703          * @param {String} value The string to encode
14704          * @return {String} The encoded text
14705          */
14706         htmlEncode : function(value){
14707             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14708         },
14709
14710         /**
14711          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14712          * @param {String} value The string to decode
14713          * @return {String} The decoded text
14714          */
14715         htmlDecode : function(value){
14716             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14717         },
14718
14719         /**
14720          * Trims any whitespace from either side of a string
14721          * @param {String} value The text to trim
14722          * @return {String} The trimmed text
14723          */
14724         trim : function(value){
14725             return String(value).replace(trimRe, "");
14726         },
14727
14728         /**
14729          * Returns a substring from within an original string
14730          * @param {String} value The original text
14731          * @param {Number} start The start index of the substring
14732          * @param {Number} length The length of the substring
14733          * @return {String} The substring
14734          */
14735         substr : function(value, start, length){
14736             return String(value).substr(start, length);
14737         },
14738
14739         /**
14740          * Converts a string to all lower case letters
14741          * @param {String} value The text to convert
14742          * @return {String} The converted text
14743          */
14744         lowercase : function(value){
14745             return String(value).toLowerCase();
14746         },
14747
14748         /**
14749          * Converts a string to all upper case letters
14750          * @param {String} value The text to convert
14751          * @return {String} The converted text
14752          */
14753         uppercase : function(value){
14754             return String(value).toUpperCase();
14755         },
14756
14757         /**
14758          * Converts the first character only of a string to upper case
14759          * @param {String} value The text to convert
14760          * @return {String} The converted text
14761          */
14762         capitalize : function(value){
14763             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14764         },
14765
14766         // private
14767         call : function(value, fn){
14768             if(arguments.length > 2){
14769                 var args = Array.prototype.slice.call(arguments, 2);
14770                 args.unshift(value);
14771                  
14772                 return /** eval:var:value */  eval(fn).apply(window, args);
14773             }else{
14774                 /** eval:var:value */
14775                 return /** eval:var:value */ eval(fn).call(window, value);
14776             }
14777         },
14778
14779        
14780         /**
14781          * safer version of Math.toFixed..??/
14782          * @param {Number/String} value The numeric value to format
14783          * @param {Number/String} value Decimal places 
14784          * @return {String} The formatted currency string
14785          */
14786         toFixed : function(v, n)
14787         {
14788             // why not use to fixed - precision is buggered???
14789             if (!n) {
14790                 return Math.round(v-0);
14791             }
14792             var fact = Math.pow(10,n+1);
14793             v = (Math.round((v-0)*fact))/fact;
14794             var z = (''+fact).substring(2);
14795             if (v == Math.floor(v)) {
14796                 return Math.floor(v) + '.' + z;
14797             }
14798             
14799             // now just padd decimals..
14800             var ps = String(v).split('.');
14801             var fd = (ps[1] + z);
14802             var r = fd.substring(0,n); 
14803             var rm = fd.substring(n); 
14804             if (rm < 5) {
14805                 return ps[0] + '.' + r;
14806             }
14807             r*=1; // turn it into a number;
14808             r++;
14809             if (String(r).length != n) {
14810                 ps[0]*=1;
14811                 ps[0]++;
14812                 r = String(r).substring(1); // chop the end off.
14813             }
14814             
14815             return ps[0] + '.' + r;
14816              
14817         },
14818         
14819         /**
14820          * Format a number as US currency
14821          * @param {Number/String} value The numeric value to format
14822          * @return {String} The formatted currency string
14823          */
14824         usMoney : function(v){
14825             return '$' + Roo.util.Format.number(v);
14826         },
14827         
14828         /**
14829          * Format a number
14830          * eventually this should probably emulate php's number_format
14831          * @param {Number/String} value The numeric value to format
14832          * @param {Number} decimals number of decimal places
14833          * @param {String} delimiter for thousands (default comma)
14834          * @return {String} The formatted currency string
14835          */
14836         number : function(v, decimals, thousandsDelimiter)
14837         {
14838             // multiply and round.
14839             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14840             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14841             
14842             var mul = Math.pow(10, decimals);
14843             var zero = String(mul).substring(1);
14844             v = (Math.round((v-0)*mul))/mul;
14845             
14846             // if it's '0' number.. then
14847             
14848             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14849             v = String(v);
14850             var ps = v.split('.');
14851             var whole = ps[0];
14852             
14853             var r = /(\d+)(\d{3})/;
14854             // add comma's
14855             
14856             if(thousandsDelimiter.length != 0) {
14857                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14858             } 
14859             
14860             var sub = ps[1] ?
14861                     // has decimals..
14862                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14863                     // does not have decimals
14864                     (decimals ? ('.' + zero) : '');
14865             
14866             
14867             return whole + sub ;
14868         },
14869         
14870         /**
14871          * Parse a value into a formatted date using the specified format pattern.
14872          * @param {Mixed} value The value to format
14873          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14874          * @return {String} The formatted date string
14875          */
14876         date : function(v, format){
14877             if(!v){
14878                 return "";
14879             }
14880             if(!(v instanceof Date)){
14881                 v = new Date(Date.parse(v));
14882             }
14883             return v.dateFormat(format || Roo.util.Format.defaults.date);
14884         },
14885
14886         /**
14887          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14888          * @param {String} format Any valid date format string
14889          * @return {Function} The date formatting function
14890          */
14891         dateRenderer : function(format){
14892             return function(v){
14893                 return Roo.util.Format.date(v, format);  
14894             };
14895         },
14896
14897         // private
14898         stripTagsRE : /<\/?[^>]+>/gi,
14899         
14900         /**
14901          * Strips all HTML tags
14902          * @param {Mixed} value The text from which to strip tags
14903          * @return {String} The stripped text
14904          */
14905         stripTags : function(v){
14906             return !v ? v : String(v).replace(this.stripTagsRE, "");
14907         },
14908         
14909         /**
14910          * Size in Mb,Gb etc.
14911          * @param {Number} value The number to be formated
14912          * @param {number} decimals how many decimal places
14913          * @return {String} the formated string
14914          */
14915         size : function(value, decimals)
14916         {
14917             var sizes = ['b', 'k', 'M', 'G', 'T'];
14918             if (value == 0) {
14919                 return 0;
14920             }
14921             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14922             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14923         }
14924         
14925         
14926         
14927     };
14928 }();
14929 Roo.util.Format.defaults = {
14930     date : 'd/M/Y'
14931 };/*
14932  * Based on:
14933  * Ext JS Library 1.1.1
14934  * Copyright(c) 2006-2007, Ext JS, LLC.
14935  *
14936  * Originally Released Under LGPL - original licence link has changed is not relivant.
14937  *
14938  * Fork - LGPL
14939  * <script type="text/javascript">
14940  */
14941
14942
14943  
14944
14945 /**
14946  * @class Roo.MasterTemplate
14947  * @extends Roo.Template
14948  * Provides a template that can have child templates. The syntax is:
14949 <pre><code>
14950 var t = new Roo.MasterTemplate(
14951         '&lt;select name="{name}"&gt;',
14952                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14953         '&lt;/select&gt;'
14954 );
14955 t.add('options', {value: 'foo', text: 'bar'});
14956 // or you can add multiple child elements in one shot
14957 t.addAll('options', [
14958     {value: 'foo', text: 'bar'},
14959     {value: 'foo2', text: 'bar2'},
14960     {value: 'foo3', text: 'bar3'}
14961 ]);
14962 // then append, applying the master template values
14963 t.append('my-form', {name: 'my-select'});
14964 </code></pre>
14965 * A name attribute for the child template is not required if you have only one child
14966 * template or you want to refer to them by index.
14967  */
14968 Roo.MasterTemplate = function(){
14969     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14970     this.originalHtml = this.html;
14971     var st = {};
14972     var m, re = this.subTemplateRe;
14973     re.lastIndex = 0;
14974     var subIndex = 0;
14975     while(m = re.exec(this.html)){
14976         var name = m[1], content = m[2];
14977         st[subIndex] = {
14978             name: name,
14979             index: subIndex,
14980             buffer: [],
14981             tpl : new Roo.Template(content)
14982         };
14983         if(name){
14984             st[name] = st[subIndex];
14985         }
14986         st[subIndex].tpl.compile();
14987         st[subIndex].tpl.call = this.call.createDelegate(this);
14988         subIndex++;
14989     }
14990     this.subCount = subIndex;
14991     this.subs = st;
14992 };
14993 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14994     /**
14995     * The regular expression used to match sub templates
14996     * @type RegExp
14997     * @property
14998     */
14999     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15000
15001     /**
15002      * Applies the passed values to a child template.
15003      * @param {String/Number} name (optional) The name or index of the child template
15004      * @param {Array/Object} values The values to be applied to the template
15005      * @return {MasterTemplate} this
15006      */
15007      add : function(name, values){
15008         if(arguments.length == 1){
15009             values = arguments[0];
15010             name = 0;
15011         }
15012         var s = this.subs[name];
15013         s.buffer[s.buffer.length] = s.tpl.apply(values);
15014         return this;
15015     },
15016
15017     /**
15018      * Applies all the passed values to a child template.
15019      * @param {String/Number} name (optional) The name or index of the child template
15020      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15021      * @param {Boolean} reset (optional) True to reset the template first
15022      * @return {MasterTemplate} this
15023      */
15024     fill : function(name, values, reset){
15025         var a = arguments;
15026         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15027             values = a[0];
15028             name = 0;
15029             reset = a[1];
15030         }
15031         if(reset){
15032             this.reset();
15033         }
15034         for(var i = 0, len = values.length; i < len; i++){
15035             this.add(name, values[i]);
15036         }
15037         return this;
15038     },
15039
15040     /**
15041      * Resets the template for reuse
15042      * @return {MasterTemplate} this
15043      */
15044      reset : function(){
15045         var s = this.subs;
15046         for(var i = 0; i < this.subCount; i++){
15047             s[i].buffer = [];
15048         }
15049         return this;
15050     },
15051
15052     applyTemplate : function(values){
15053         var s = this.subs;
15054         var replaceIndex = -1;
15055         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15056             return s[++replaceIndex].buffer.join("");
15057         });
15058         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15059     },
15060
15061     apply : function(){
15062         return this.applyTemplate.apply(this, arguments);
15063     },
15064
15065     compile : function(){return this;}
15066 });
15067
15068 /**
15069  * Alias for fill().
15070  * @method
15071  */
15072 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15073  /**
15074  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15075  * var tpl = Roo.MasterTemplate.from('element-id');
15076  * @param {String/HTMLElement} el
15077  * @param {Object} config
15078  * @static
15079  */
15080 Roo.MasterTemplate.from = function(el, config){
15081     el = Roo.getDom(el);
15082     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15083 };/*
15084  * Based on:
15085  * Ext JS Library 1.1.1
15086  * Copyright(c) 2006-2007, Ext JS, LLC.
15087  *
15088  * Originally Released Under LGPL - original licence link has changed is not relivant.
15089  *
15090  * Fork - LGPL
15091  * <script type="text/javascript">
15092  */
15093
15094  
15095 /**
15096  * @class Roo.util.CSS
15097  * Utility class for manipulating CSS rules
15098  * @static
15099
15100  */
15101 Roo.util.CSS = function(){
15102         var rules = null;
15103         var doc = document;
15104
15105     var camelRe = /(-[a-z])/gi;
15106     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15107
15108    return {
15109    /**
15110     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15111     * tag and appended to the HEAD of the document.
15112     * @param {String|Object} cssText The text containing the css rules
15113     * @param {String} id An id to add to the stylesheet for later removal
15114     * @return {StyleSheet}
15115     */
15116     createStyleSheet : function(cssText, id){
15117         var ss;
15118         var head = doc.getElementsByTagName("head")[0];
15119         var nrules = doc.createElement("style");
15120         nrules.setAttribute("type", "text/css");
15121         if(id){
15122             nrules.setAttribute("id", id);
15123         }
15124         if (typeof(cssText) != 'string') {
15125             // support object maps..
15126             // not sure if this a good idea.. 
15127             // perhaps it should be merged with the general css handling
15128             // and handle js style props.
15129             var cssTextNew = [];
15130             for(var n in cssText) {
15131                 var citems = [];
15132                 for(var k in cssText[n]) {
15133                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15134                 }
15135                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15136                 
15137             }
15138             cssText = cssTextNew.join("\n");
15139             
15140         }
15141        
15142        
15143        if(Roo.isIE){
15144            head.appendChild(nrules);
15145            ss = nrules.styleSheet;
15146            ss.cssText = cssText;
15147        }else{
15148            try{
15149                 nrules.appendChild(doc.createTextNode(cssText));
15150            }catch(e){
15151                nrules.cssText = cssText; 
15152            }
15153            head.appendChild(nrules);
15154            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15155        }
15156        this.cacheStyleSheet(ss);
15157        return ss;
15158    },
15159
15160    /**
15161     * Removes a style or link tag by id
15162     * @param {String} id The id of the tag
15163     */
15164    removeStyleSheet : function(id){
15165        var existing = doc.getElementById(id);
15166        if(existing){
15167            existing.parentNode.removeChild(existing);
15168        }
15169    },
15170
15171    /**
15172     * Dynamically swaps an existing stylesheet reference for a new one
15173     * @param {String} id The id of an existing link tag to remove
15174     * @param {String} url The href of the new stylesheet to include
15175     */
15176    swapStyleSheet : function(id, url){
15177        this.removeStyleSheet(id);
15178        var ss = doc.createElement("link");
15179        ss.setAttribute("rel", "stylesheet");
15180        ss.setAttribute("type", "text/css");
15181        ss.setAttribute("id", id);
15182        ss.setAttribute("href", url);
15183        doc.getElementsByTagName("head")[0].appendChild(ss);
15184    },
15185    
15186    /**
15187     * Refresh the rule cache if you have dynamically added stylesheets
15188     * @return {Object} An object (hash) of rules indexed by selector
15189     */
15190    refreshCache : function(){
15191        return this.getRules(true);
15192    },
15193
15194    // private
15195    cacheStyleSheet : function(stylesheet){
15196        if(!rules){
15197            rules = {};
15198        }
15199        try{// try catch for cross domain access issue
15200            var ssRules = stylesheet.cssRules || stylesheet.rules;
15201            for(var j = ssRules.length-1; j >= 0; --j){
15202                rules[ssRules[j].selectorText] = ssRules[j];
15203            }
15204        }catch(e){}
15205    },
15206    
15207    /**
15208     * Gets all css rules for the document
15209     * @param {Boolean} refreshCache true to refresh the internal cache
15210     * @return {Object} An object (hash) of rules indexed by selector
15211     */
15212    getRules : function(refreshCache){
15213                 if(rules == null || refreshCache){
15214                         rules = {};
15215                         var ds = doc.styleSheets;
15216                         for(var i =0, len = ds.length; i < len; i++){
15217                             try{
15218                         this.cacheStyleSheet(ds[i]);
15219                     }catch(e){} 
15220                 }
15221                 }
15222                 return rules;
15223         },
15224         
15225         /**
15226     * Gets an an individual CSS rule by selector(s)
15227     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15228     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15229     * @return {CSSRule} The CSS rule or null if one is not found
15230     */
15231    getRule : function(selector, refreshCache){
15232                 var rs = this.getRules(refreshCache);
15233                 if(!(selector instanceof Array)){
15234                     return rs[selector];
15235                 }
15236                 for(var i = 0; i < selector.length; i++){
15237                         if(rs[selector[i]]){
15238                                 return rs[selector[i]];
15239                         }
15240                 }
15241                 return null;
15242         },
15243         
15244         
15245         /**
15246     * Updates a rule property
15247     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15248     * @param {String} property The css property
15249     * @param {String} value The new value for the property
15250     * @return {Boolean} true If a rule was found and updated
15251     */
15252    updateRule : function(selector, property, value){
15253                 if(!(selector instanceof Array)){
15254                         var rule = this.getRule(selector);
15255                         if(rule){
15256                                 rule.style[property.replace(camelRe, camelFn)] = value;
15257                                 return true;
15258                         }
15259                 }else{
15260                         for(var i = 0; i < selector.length; i++){
15261                                 if(this.updateRule(selector[i], property, value)){
15262                                         return true;
15263                                 }
15264                         }
15265                 }
15266                 return false;
15267         }
15268    };   
15269 }();/*
15270  * Based on:
15271  * Ext JS Library 1.1.1
15272  * Copyright(c) 2006-2007, Ext JS, LLC.
15273  *
15274  * Originally Released Under LGPL - original licence link has changed is not relivant.
15275  *
15276  * Fork - LGPL
15277  * <script type="text/javascript">
15278  */
15279
15280  
15281
15282 /**
15283  * @class Roo.util.ClickRepeater
15284  * @extends Roo.util.Observable
15285  * 
15286  * A wrapper class which can be applied to any element. Fires a "click" event while the
15287  * mouse is pressed. The interval between firings may be specified in the config but
15288  * defaults to 10 milliseconds.
15289  * 
15290  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15291  * 
15292  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15293  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15294  * Similar to an autorepeat key delay.
15295  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15296  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15297  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15298  *           "interval" and "delay" are ignored. "immediate" is honored.
15299  * @cfg {Boolean} preventDefault True to prevent the default click event
15300  * @cfg {Boolean} stopDefault True to stop the default click event
15301  * 
15302  * @history
15303  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15304  *     2007-02-02 jvs Renamed to ClickRepeater
15305  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15306  *
15307  *  @constructor
15308  * @param {String/HTMLElement/Element} el The element to listen on
15309  * @param {Object} config
15310  **/
15311 Roo.util.ClickRepeater = function(el, config)
15312 {
15313     this.el = Roo.get(el);
15314     this.el.unselectable();
15315
15316     Roo.apply(this, config);
15317
15318     this.addEvents({
15319     /**
15320      * @event mousedown
15321      * Fires when the mouse button is depressed.
15322      * @param {Roo.util.ClickRepeater} this
15323      */
15324         "mousedown" : true,
15325     /**
15326      * @event click
15327      * Fires on a specified interval during the time the element is pressed.
15328      * @param {Roo.util.ClickRepeater} this
15329      */
15330         "click" : true,
15331     /**
15332      * @event mouseup
15333      * Fires when the mouse key is released.
15334      * @param {Roo.util.ClickRepeater} this
15335      */
15336         "mouseup" : true
15337     });
15338
15339     this.el.on("mousedown", this.handleMouseDown, this);
15340     if(this.preventDefault || this.stopDefault){
15341         this.el.on("click", function(e){
15342             if(this.preventDefault){
15343                 e.preventDefault();
15344             }
15345             if(this.stopDefault){
15346                 e.stopEvent();
15347             }
15348         }, this);
15349     }
15350
15351     // allow inline handler
15352     if(this.handler){
15353         this.on("click", this.handler,  this.scope || this);
15354     }
15355
15356     Roo.util.ClickRepeater.superclass.constructor.call(this);
15357 };
15358
15359 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15360     interval : 20,
15361     delay: 250,
15362     preventDefault : true,
15363     stopDefault : false,
15364     timer : 0,
15365
15366     // private
15367     handleMouseDown : function(){
15368         clearTimeout(this.timer);
15369         this.el.blur();
15370         if(this.pressClass){
15371             this.el.addClass(this.pressClass);
15372         }
15373         this.mousedownTime = new Date();
15374
15375         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15376         this.el.on("mouseout", this.handleMouseOut, this);
15377
15378         this.fireEvent("mousedown", this);
15379         this.fireEvent("click", this);
15380         
15381         this.timer = this.click.defer(this.delay || this.interval, this);
15382     },
15383
15384     // private
15385     click : function(){
15386         this.fireEvent("click", this);
15387         this.timer = this.click.defer(this.getInterval(), this);
15388     },
15389
15390     // private
15391     getInterval: function(){
15392         if(!this.accelerate){
15393             return this.interval;
15394         }
15395         var pressTime = this.mousedownTime.getElapsed();
15396         if(pressTime < 500){
15397             return 400;
15398         }else if(pressTime < 1700){
15399             return 320;
15400         }else if(pressTime < 2600){
15401             return 250;
15402         }else if(pressTime < 3500){
15403             return 180;
15404         }else if(pressTime < 4400){
15405             return 140;
15406         }else if(pressTime < 5300){
15407             return 80;
15408         }else if(pressTime < 6200){
15409             return 50;
15410         }else{
15411             return 10;
15412         }
15413     },
15414
15415     // private
15416     handleMouseOut : function(){
15417         clearTimeout(this.timer);
15418         if(this.pressClass){
15419             this.el.removeClass(this.pressClass);
15420         }
15421         this.el.on("mouseover", this.handleMouseReturn, this);
15422     },
15423
15424     // private
15425     handleMouseReturn : function(){
15426         this.el.un("mouseover", this.handleMouseReturn);
15427         if(this.pressClass){
15428             this.el.addClass(this.pressClass);
15429         }
15430         this.click();
15431     },
15432
15433     // private
15434     handleMouseUp : function(){
15435         clearTimeout(this.timer);
15436         this.el.un("mouseover", this.handleMouseReturn);
15437         this.el.un("mouseout", this.handleMouseOut);
15438         Roo.get(document).un("mouseup", this.handleMouseUp);
15439         this.el.removeClass(this.pressClass);
15440         this.fireEvent("mouseup", this);
15441     }
15442 });/**
15443  * @class Roo.util.Clipboard
15444  * @static
15445  * 
15446  * Clipboard UTILS
15447  * 
15448  **/
15449 Roo.util.Clipboard = {
15450     /**
15451      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15452      * @param {String} text to copy to clipboard
15453      */
15454     write : function(text) {
15455         // navigator clipboard api needs a secure context (https)
15456         if (navigator.clipboard && window.isSecureContext) {
15457             // navigator clipboard api method'
15458             navigator.clipboard.writeText(text);
15459             return ;
15460         } 
15461         // text area method
15462         var ta = document.createElement("textarea");
15463         ta.value = text;
15464         // make the textarea out of viewport
15465         ta.style.position = "fixed";
15466         ta.style.left = "-999999px";
15467         ta.style.top = "-999999px";
15468         document.body.appendChild(ta);
15469         ta.focus();
15470         ta.select();
15471         document.execCommand('copy');
15472         (function() {
15473             ta.remove();
15474         }).defer(100);
15475         
15476     }
15477         
15478 }
15479     /*
15480  * Based on:
15481  * Ext JS Library 1.1.1
15482  * Copyright(c) 2006-2007, Ext JS, LLC.
15483  *
15484  * Originally Released Under LGPL - original licence link has changed is not relivant.
15485  *
15486  * Fork - LGPL
15487  * <script type="text/javascript">
15488  */
15489
15490  
15491 /**
15492  * @class Roo.KeyNav
15493  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15494  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15495  * way to implement custom navigation schemes for any UI component.</p>
15496  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15497  * pageUp, pageDown, del, home, end.  Usage:</p>
15498  <pre><code>
15499 var nav = new Roo.KeyNav("my-element", {
15500     "left" : function(e){
15501         this.moveLeft(e.ctrlKey);
15502     },
15503     "right" : function(e){
15504         this.moveRight(e.ctrlKey);
15505     },
15506     "enter" : function(e){
15507         this.save();
15508     },
15509     scope : this
15510 });
15511 </code></pre>
15512  * @constructor
15513  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15514  * @param {Object} config The config
15515  */
15516 Roo.KeyNav = function(el, config){
15517     this.el = Roo.get(el);
15518     Roo.apply(this, config);
15519     if(!this.disabled){
15520         this.disabled = true;
15521         this.enable();
15522     }
15523 };
15524
15525 Roo.KeyNav.prototype = {
15526     /**
15527      * @cfg {Boolean} disabled
15528      * True to disable this KeyNav instance (defaults to false)
15529      */
15530     disabled : false,
15531     /**
15532      * @cfg {String} defaultEventAction
15533      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15534      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15535      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15536      */
15537     defaultEventAction: "stopEvent",
15538     /**
15539      * @cfg {Boolean} forceKeyDown
15540      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15541      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15542      * handle keydown instead of keypress.
15543      */
15544     forceKeyDown : false,
15545
15546     // private
15547     prepareEvent : function(e){
15548         var k = e.getKey();
15549         var h = this.keyToHandler[k];
15550         //if(h && this[h]){
15551         //    e.stopPropagation();
15552         //}
15553         if(Roo.isSafari && h && k >= 37 && k <= 40){
15554             e.stopEvent();
15555         }
15556     },
15557
15558     // private
15559     relay : function(e){
15560         var k = e.getKey();
15561         var h = this.keyToHandler[k];
15562         if(h && this[h]){
15563             if(this.doRelay(e, this[h], h) !== true){
15564                 e[this.defaultEventAction]();
15565             }
15566         }
15567     },
15568
15569     // private
15570     doRelay : function(e, h, hname){
15571         return h.call(this.scope || this, e);
15572     },
15573
15574     // possible handlers
15575     enter : false,
15576     left : false,
15577     right : false,
15578     up : false,
15579     down : false,
15580     tab : false,
15581     esc : false,
15582     pageUp : false,
15583     pageDown : false,
15584     del : false,
15585     home : false,
15586     end : false,
15587
15588     // quick lookup hash
15589     keyToHandler : {
15590         37 : "left",
15591         39 : "right",
15592         38 : "up",
15593         40 : "down",
15594         33 : "pageUp",
15595         34 : "pageDown",
15596         46 : "del",
15597         36 : "home",
15598         35 : "end",
15599         13 : "enter",
15600         27 : "esc",
15601         9  : "tab"
15602     },
15603
15604         /**
15605          * Enable this KeyNav
15606          */
15607         enable: function(){
15608                 if(this.disabled){
15609             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15610             // the EventObject will normalize Safari automatically
15611             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15612                 this.el.on("keydown", this.relay,  this);
15613             }else{
15614                 this.el.on("keydown", this.prepareEvent,  this);
15615                 this.el.on("keypress", this.relay,  this);
15616             }
15617                     this.disabled = false;
15618                 }
15619         },
15620
15621         /**
15622          * Disable this KeyNav
15623          */
15624         disable: function(){
15625                 if(!this.disabled){
15626                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15627                 this.el.un("keydown", this.relay);
15628             }else{
15629                 this.el.un("keydown", this.prepareEvent);
15630                 this.el.un("keypress", this.relay);
15631             }
15632                     this.disabled = true;
15633                 }
15634         }
15635 };/*
15636  * Based on:
15637  * Ext JS Library 1.1.1
15638  * Copyright(c) 2006-2007, Ext JS, LLC.
15639  *
15640  * Originally Released Under LGPL - original licence link has changed is not relivant.
15641  *
15642  * Fork - LGPL
15643  * <script type="text/javascript">
15644  */
15645
15646  
15647 /**
15648  * @class Roo.KeyMap
15649  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15650  * The constructor accepts the same config object as defined by {@link #addBinding}.
15651  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15652  * combination it will call the function with this signature (if the match is a multi-key
15653  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15654  * A KeyMap can also handle a string representation of keys.<br />
15655  * Usage:
15656  <pre><code>
15657 // map one key by key code
15658 var map = new Roo.KeyMap("my-element", {
15659     key: 13, // or Roo.EventObject.ENTER
15660     fn: myHandler,
15661     scope: myObject
15662 });
15663
15664 // map multiple keys to one action by string
15665 var map = new Roo.KeyMap("my-element", {
15666     key: "a\r\n\t",
15667     fn: myHandler,
15668     scope: myObject
15669 });
15670
15671 // map multiple keys to multiple actions by strings and array of codes
15672 var map = new Roo.KeyMap("my-element", [
15673     {
15674         key: [10,13],
15675         fn: function(){ alert("Return was pressed"); }
15676     }, {
15677         key: "abc",
15678         fn: function(){ alert('a, b or c was pressed'); }
15679     }, {
15680         key: "\t",
15681         ctrl:true,
15682         shift:true,
15683         fn: function(){ alert('Control + shift + tab was pressed.'); }
15684     }
15685 ]);
15686 </code></pre>
15687  * <b>Note: A KeyMap starts enabled</b>
15688  * @constructor
15689  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15690  * @param {Object} config The config (see {@link #addBinding})
15691  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15692  */
15693 Roo.KeyMap = function(el, config, eventName){
15694     this.el  = Roo.get(el);
15695     this.eventName = eventName || "keydown";
15696     this.bindings = [];
15697     if(config){
15698         this.addBinding(config);
15699     }
15700     this.enable();
15701 };
15702
15703 Roo.KeyMap.prototype = {
15704     /**
15705      * True to stop the event from bubbling and prevent the default browser action if the
15706      * key was handled by the KeyMap (defaults to false)
15707      * @type Boolean
15708      */
15709     stopEvent : false,
15710
15711     /**
15712      * Add a new binding to this KeyMap. The following config object properties are supported:
15713      * <pre>
15714 Property    Type             Description
15715 ----------  ---------------  ----------------------------------------------------------------------
15716 key         String/Array     A single keycode or an array of keycodes to handle
15717 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15718 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15719 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15720 fn          Function         The function to call when KeyMap finds the expected key combination
15721 scope       Object           The scope of the callback function
15722 </pre>
15723      *
15724      * Usage:
15725      * <pre><code>
15726 // Create a KeyMap
15727 var map = new Roo.KeyMap(document, {
15728     key: Roo.EventObject.ENTER,
15729     fn: handleKey,
15730     scope: this
15731 });
15732
15733 //Add a new binding to the existing KeyMap later
15734 map.addBinding({
15735     key: 'abc',
15736     shift: true,
15737     fn: handleKey,
15738     scope: this
15739 });
15740 </code></pre>
15741      * @param {Object/Array} config A single KeyMap config or an array of configs
15742      */
15743         addBinding : function(config){
15744         if(config instanceof Array){
15745             for(var i = 0, len = config.length; i < len; i++){
15746                 this.addBinding(config[i]);
15747             }
15748             return;
15749         }
15750         var keyCode = config.key,
15751             shift = config.shift, 
15752             ctrl = config.ctrl, 
15753             alt = config.alt,
15754             fn = config.fn,
15755             scope = config.scope;
15756         if(typeof keyCode == "string"){
15757             var ks = [];
15758             var keyString = keyCode.toUpperCase();
15759             for(var j = 0, len = keyString.length; j < len; j++){
15760                 ks.push(keyString.charCodeAt(j));
15761             }
15762             keyCode = ks;
15763         }
15764         var keyArray = keyCode instanceof Array;
15765         var handler = function(e){
15766             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15767                 var k = e.getKey();
15768                 if(keyArray){
15769                     for(var i = 0, len = keyCode.length; i < len; i++){
15770                         if(keyCode[i] == k){
15771                           if(this.stopEvent){
15772                               e.stopEvent();
15773                           }
15774                           fn.call(scope || window, k, e);
15775                           return;
15776                         }
15777                     }
15778                 }else{
15779                     if(k == keyCode){
15780                         if(this.stopEvent){
15781                            e.stopEvent();
15782                         }
15783                         fn.call(scope || window, k, e);
15784                     }
15785                 }
15786             }
15787         };
15788         this.bindings.push(handler);  
15789         },
15790
15791     /**
15792      * Shorthand for adding a single key listener
15793      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15794      * following options:
15795      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15796      * @param {Function} fn The function to call
15797      * @param {Object} scope (optional) The scope of the function
15798      */
15799     on : function(key, fn, scope){
15800         var keyCode, shift, ctrl, alt;
15801         if(typeof key == "object" && !(key instanceof Array)){
15802             keyCode = key.key;
15803             shift = key.shift;
15804             ctrl = key.ctrl;
15805             alt = key.alt;
15806         }else{
15807             keyCode = key;
15808         }
15809         this.addBinding({
15810             key: keyCode,
15811             shift: shift,
15812             ctrl: ctrl,
15813             alt: alt,
15814             fn: fn,
15815             scope: scope
15816         })
15817     },
15818
15819     // private
15820     handleKeyDown : function(e){
15821             if(this.enabled){ //just in case
15822             var b = this.bindings;
15823             for(var i = 0, len = b.length; i < len; i++){
15824                 b[i].call(this, e);
15825             }
15826             }
15827         },
15828         
15829         /**
15830          * Returns true if this KeyMap is enabled
15831          * @return {Boolean} 
15832          */
15833         isEnabled : function(){
15834             return this.enabled;  
15835         },
15836         
15837         /**
15838          * Enables this KeyMap
15839          */
15840         enable: function(){
15841                 if(!this.enabled){
15842                     this.el.on(this.eventName, this.handleKeyDown, this);
15843                     this.enabled = true;
15844                 }
15845         },
15846
15847         /**
15848          * Disable this KeyMap
15849          */
15850         disable: function(){
15851                 if(this.enabled){
15852                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15853                     this.enabled = false;
15854                 }
15855         }
15856 };/*
15857  * Based on:
15858  * Ext JS Library 1.1.1
15859  * Copyright(c) 2006-2007, Ext JS, LLC.
15860  *
15861  * Originally Released Under LGPL - original licence link has changed is not relivant.
15862  *
15863  * Fork - LGPL
15864  * <script type="text/javascript">
15865  */
15866
15867  
15868 /**
15869  * @class Roo.util.TextMetrics
15870  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15871  * wide, in pixels, a given block of text will be.
15872  * @static
15873  */
15874 Roo.util.TextMetrics = function(){
15875     var shared;
15876     return {
15877         /**
15878          * Measures the size of the specified text
15879          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15880          * that can affect the size of the rendered text
15881          * @param {String} text The text to measure
15882          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15883          * in order to accurately measure the text height
15884          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15885          */
15886         measure : function(el, text, fixedWidth){
15887             if(!shared){
15888                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15889             }
15890             shared.bind(el);
15891             shared.setFixedWidth(fixedWidth || 'auto');
15892             return shared.getSize(text);
15893         },
15894
15895         /**
15896          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15897          * the overhead of multiple calls to initialize the style properties on each measurement.
15898          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15899          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15900          * in order to accurately measure the text height
15901          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15902          */
15903         createInstance : function(el, fixedWidth){
15904             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15905         }
15906     };
15907 }();
15908
15909 /**
15910  * @class Roo.util.TextMetrics.Instance
15911  * Instance of  TextMetrics Calcuation
15912  * @constructor
15913  * Create a new TextMetrics Instance
15914  * @param {Object} bindto
15915  * @param {Boolean} fixedWidth
15916  */
15917
15918 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15919 {
15920     var ml = new Roo.Element(document.createElement('div'));
15921     document.body.appendChild(ml.dom);
15922     ml.position('absolute');
15923     ml.setLeftTop(-1000, -1000);
15924     ml.hide();
15925
15926     if(fixedWidth){
15927         ml.setWidth(fixedWidth);
15928     }
15929      
15930     var instance = {
15931         /**
15932          * Returns the size of the specified text based on the internal element's style and width properties
15933          * @param {String} text The text to measure
15934          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15935          */
15936         getSize : function(text){
15937             ml.update(text);
15938             var s = ml.getSize();
15939             ml.update('');
15940             return s;
15941         },
15942
15943         /**
15944          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15945          * that can affect the size of the rendered text
15946          * @param {String/HTMLElement} el The element, dom node or id
15947          */
15948         bind : function(el){
15949             ml.setStyle(
15950                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15951             );
15952         },
15953
15954         /**
15955          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15956          * to set a fixed width in order to accurately measure the text height.
15957          * @param {Number} width The width to set on the element
15958          */
15959         setFixedWidth : function(width){
15960             ml.setWidth(width);
15961         },
15962
15963         /**
15964          * Returns the measured width of the specified text
15965          * @param {String} text The text to measure
15966          * @return {Number} width The width in pixels
15967          */
15968         getWidth : function(text){
15969             ml.dom.style.width = 'auto';
15970             return this.getSize(text).width;
15971         },
15972
15973         /**
15974          * Returns the measured height of the specified text.  For multiline text, be sure to call
15975          * {@link #setFixedWidth} if necessary.
15976          * @param {String} text The text to measure
15977          * @return {Number} height The height in pixels
15978          */
15979         getHeight : function(text){
15980             return this.getSize(text).height;
15981         }
15982     };
15983
15984     instance.bind(bindTo);
15985
15986     return instance;
15987 };
15988
15989 // backwards compat
15990 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15991  * Based on:
15992  * Ext JS Library 1.1.1
15993  * Copyright(c) 2006-2007, Ext JS, LLC.
15994  *
15995  * Originally Released Under LGPL - original licence link has changed is not relivant.
15996  *
15997  * Fork - LGPL
15998  * <script type="text/javascript">
15999  */
16000
16001 /**
16002  * @class Roo.state.Provider
16003  * Abstract base class for state provider implementations. This class provides methods
16004  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16005  * Provider interface.
16006  */
16007 Roo.state.Provider = function(){
16008     /**
16009      * @event statechange
16010      * Fires when a state change occurs.
16011      * @param {Provider} this This state provider
16012      * @param {String} key The state key which was changed
16013      * @param {String} value The encoded value for the state
16014      */
16015     this.addEvents({
16016         "statechange": true
16017     });
16018     this.state = {};
16019     Roo.state.Provider.superclass.constructor.call(this);
16020 };
16021 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16022     /**
16023      * Returns the current value for a key
16024      * @param {String} name The key name
16025      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16026      * @return {Mixed} The state data
16027      */
16028     get : function(name, defaultValue){
16029         return typeof this.state[name] == "undefined" ?
16030             defaultValue : this.state[name];
16031     },
16032     
16033     /**
16034      * Clears a value from the state
16035      * @param {String} name The key name
16036      */
16037     clear : function(name){
16038         delete this.state[name];
16039         this.fireEvent("statechange", this, name, null);
16040     },
16041     
16042     /**
16043      * Sets the value for a key
16044      * @param {String} name The key name
16045      * @param {Mixed} value The value to set
16046      */
16047     set : function(name, value){
16048         this.state[name] = value;
16049         this.fireEvent("statechange", this, name, value);
16050     },
16051     
16052     /**
16053      * Decodes a string previously encoded with {@link #encodeValue}.
16054      * @param {String} value The value to decode
16055      * @return {Mixed} The decoded value
16056      */
16057     decodeValue : function(cookie){
16058         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16059         var matches = re.exec(unescape(cookie));
16060         if(!matches || !matches[1]) {
16061             return; // non state cookie
16062         }
16063         var type = matches[1];
16064         var v = matches[2];
16065         switch(type){
16066             case "n":
16067                 return parseFloat(v);
16068             case "d":
16069                 return new Date(Date.parse(v));
16070             case "b":
16071                 return (v == "1");
16072             case "a":
16073                 var all = [];
16074                 var values = v.split("^");
16075                 for(var i = 0, len = values.length; i < len; i++){
16076                     all.push(this.decodeValue(values[i]));
16077                 }
16078                 return all;
16079            case "o":
16080                 var all = {};
16081                 var values = v.split("^");
16082                 for(var i = 0, len = values.length; i < len; i++){
16083                     var kv = values[i].split("=");
16084                     all[kv[0]] = this.decodeValue(kv[1]);
16085                 }
16086                 return all;
16087            default:
16088                 return v;
16089         }
16090     },
16091     
16092     /**
16093      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16094      * @param {Mixed} value The value to encode
16095      * @return {String} The encoded value
16096      */
16097     encodeValue : function(v){
16098         var enc;
16099         if(typeof v == "number"){
16100             enc = "n:" + v;
16101         }else if(typeof v == "boolean"){
16102             enc = "b:" + (v ? "1" : "0");
16103         }else if(v instanceof Date){
16104             enc = "d:" + v.toGMTString();
16105         }else if(v instanceof Array){
16106             var flat = "";
16107             for(var i = 0, len = v.length; i < len; i++){
16108                 flat += this.encodeValue(v[i]);
16109                 if(i != len-1) {
16110                     flat += "^";
16111                 }
16112             }
16113             enc = "a:" + flat;
16114         }else if(typeof v == "object"){
16115             var flat = "";
16116             for(var key in v){
16117                 if(typeof v[key] != "function"){
16118                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16119                 }
16120             }
16121             enc = "o:" + flat.substring(0, flat.length-1);
16122         }else{
16123             enc = "s:" + v;
16124         }
16125         return escape(enc);        
16126     }
16127 });
16128
16129 /*
16130  * Based on:
16131  * Ext JS Library 1.1.1
16132  * Copyright(c) 2006-2007, Ext JS, LLC.
16133  *
16134  * Originally Released Under LGPL - original licence link has changed is not relivant.
16135  *
16136  * Fork - LGPL
16137  * <script type="text/javascript">
16138  */
16139 /**
16140  * @class Roo.state.Manager
16141  * This is the global state manager. By default all components that are "state aware" check this class
16142  * for state information if you don't pass them a custom state provider. In order for this class
16143  * to be useful, it must be initialized with a provider when your application initializes.
16144  <pre><code>
16145 // in your initialization function
16146 init : function(){
16147    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16148    ...
16149    // supposed you have a {@link Roo.BorderLayout}
16150    var layout = new Roo.BorderLayout(...);
16151    layout.restoreState();
16152    // or a {Roo.BasicDialog}
16153    var dialog = new Roo.BasicDialog(...);
16154    dialog.restoreState();
16155  </code></pre>
16156  * @static
16157  */
16158 Roo.state.Manager = function(){
16159     var provider = new Roo.state.Provider();
16160     
16161     return {
16162         /**
16163          * Configures the default state provider for your application
16164          * @param {Provider} stateProvider The state provider to set
16165          */
16166         setProvider : function(stateProvider){
16167             provider = stateProvider;
16168         },
16169         
16170         /**
16171          * Returns the current value for a key
16172          * @param {String} name The key name
16173          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16174          * @return {Mixed} The state data
16175          */
16176         get : function(key, defaultValue){
16177             return provider.get(key, defaultValue);
16178         },
16179         
16180         /**
16181          * Sets the value for a key
16182          * @param {String} name The key name
16183          * @param {Mixed} value The state data
16184          */
16185          set : function(key, value){
16186             provider.set(key, value);
16187         },
16188         
16189         /**
16190          * Clears a value from the state
16191          * @param {String} name The key name
16192          */
16193         clear : function(key){
16194             provider.clear(key);
16195         },
16196         
16197         /**
16198          * Gets the currently configured state provider
16199          * @return {Provider} The state provider
16200          */
16201         getProvider : function(){
16202             return provider;
16203         }
16204     };
16205 }();
16206 /*
16207  * Based on:
16208  * Ext JS Library 1.1.1
16209  * Copyright(c) 2006-2007, Ext JS, LLC.
16210  *
16211  * Originally Released Under LGPL - original licence link has changed is not relivant.
16212  *
16213  * Fork - LGPL
16214  * <script type="text/javascript">
16215  */
16216 /**
16217  * @class Roo.state.CookieProvider
16218  * @extends Roo.state.Provider
16219  * The default Provider implementation which saves state via cookies.
16220  * <br />Usage:
16221  <pre><code>
16222    var cp = new Roo.state.CookieProvider({
16223        path: "/cgi-bin/",
16224        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16225        domain: "roojs.com"
16226    })
16227    Roo.state.Manager.setProvider(cp);
16228  </code></pre>
16229  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16230  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16231  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16232  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16233  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16234  * domain the page is running on including the 'www' like 'www.roojs.com')
16235  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16236  * @constructor
16237  * Create a new CookieProvider
16238  * @param {Object} config The configuration object
16239  */
16240 Roo.state.CookieProvider = function(config){
16241     Roo.state.CookieProvider.superclass.constructor.call(this);
16242     this.path = "/";
16243     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16244     this.domain = null;
16245     this.secure = false;
16246     Roo.apply(this, config);
16247     this.state = this.readCookies();
16248 };
16249
16250 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16251     // private
16252     set : function(name, value){
16253         if(typeof value == "undefined" || value === null){
16254             this.clear(name);
16255             return;
16256         }
16257         this.setCookie(name, value);
16258         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16259     },
16260
16261     // private
16262     clear : function(name){
16263         this.clearCookie(name);
16264         Roo.state.CookieProvider.superclass.clear.call(this, name);
16265     },
16266
16267     // private
16268     readCookies : function(){
16269         var cookies = {};
16270         var c = document.cookie + ";";
16271         var re = /\s?(.*?)=(.*?);/g;
16272         var matches;
16273         while((matches = re.exec(c)) != null){
16274             var name = matches[1];
16275             var value = matches[2];
16276             if(name && name.substring(0,3) == "ys-"){
16277                 cookies[name.substr(3)] = this.decodeValue(value);
16278             }
16279         }
16280         return cookies;
16281     },
16282
16283     // private
16284     setCookie : function(name, value){
16285         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16286            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16287            ((this.path == null) ? "" : ("; path=" + this.path)) +
16288            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16289            ((this.secure == true) ? "; secure" : "");
16290     },
16291
16292     // private
16293     clearCookie : function(name){
16294         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16295            ((this.path == null) ? "" : ("; path=" + this.path)) +
16296            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16297            ((this.secure == true) ? "; secure" : "");
16298     }
16299 });/*
16300  * Based on:
16301  * Ext JS Library 1.1.1
16302  * Copyright(c) 2006-2007, Ext JS, LLC.
16303  *
16304  * Originally Released Under LGPL - original licence link has changed is not relivant.
16305  *
16306  * Fork - LGPL
16307  * <script type="text/javascript">
16308  */
16309  
16310
16311 /**
16312  * @class Roo.ComponentMgr
16313  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16314  * @static
16315  */
16316 Roo.ComponentMgr = function(){
16317     var all = new Roo.util.MixedCollection();
16318
16319     return {
16320         /**
16321          * Registers a component.
16322          * @param {Roo.Component} c The component
16323          */
16324         register : function(c){
16325             all.add(c);
16326         },
16327
16328         /**
16329          * Unregisters a component.
16330          * @param {Roo.Component} c The component
16331          */
16332         unregister : function(c){
16333             all.remove(c);
16334         },
16335
16336         /**
16337          * Returns a component by id
16338          * @param {String} id The component id
16339          */
16340         get : function(id){
16341             return all.get(id);
16342         },
16343
16344         /**
16345          * Registers a function that will be called when a specified component is added to ComponentMgr
16346          * @param {String} id The component id
16347          * @param {Funtction} fn The callback function
16348          * @param {Object} scope The scope of the callback
16349          */
16350         onAvailable : function(id, fn, scope){
16351             all.on("add", function(index, o){
16352                 if(o.id == id){
16353                     fn.call(scope || o, o);
16354                     all.un("add", fn, scope);
16355                 }
16356             });
16357         }
16358     };
16359 }();/*
16360  * Based on:
16361  * Ext JS Library 1.1.1
16362  * Copyright(c) 2006-2007, Ext JS, LLC.
16363  *
16364  * Originally Released Under LGPL - original licence link has changed is not relivant.
16365  *
16366  * Fork - LGPL
16367  * <script type="text/javascript">
16368  */
16369  
16370 /**
16371  * @class Roo.Component
16372  * @extends Roo.util.Observable
16373  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16374  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16375  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16376  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16377  * All visual components (widgets) that require rendering into a layout should subclass Component.
16378  * @constructor
16379  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16380  * 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
16381  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16382  */
16383 Roo.Component = function(config){
16384     config = config || {};
16385     if(config.tagName || config.dom || typeof config == "string"){ // element object
16386         config = {el: config, id: config.id || config};
16387     }
16388     this.initialConfig = config;
16389
16390     Roo.apply(this, config);
16391     this.addEvents({
16392         /**
16393          * @event disable
16394          * Fires after the component is disabled.
16395              * @param {Roo.Component} this
16396              */
16397         disable : true,
16398         /**
16399          * @event enable
16400          * Fires after the component is enabled.
16401              * @param {Roo.Component} this
16402              */
16403         enable : true,
16404         /**
16405          * @event beforeshow
16406          * Fires before the component is shown.  Return false to stop the show.
16407              * @param {Roo.Component} this
16408              */
16409         beforeshow : true,
16410         /**
16411          * @event show
16412          * Fires after the component is shown.
16413              * @param {Roo.Component} this
16414              */
16415         show : true,
16416         /**
16417          * @event beforehide
16418          * Fires before the component is hidden. Return false to stop the hide.
16419              * @param {Roo.Component} this
16420              */
16421         beforehide : true,
16422         /**
16423          * @event hide
16424          * Fires after the component is hidden.
16425              * @param {Roo.Component} this
16426              */
16427         hide : true,
16428         /**
16429          * @event beforerender
16430          * Fires before the component is rendered. Return false to stop the render.
16431              * @param {Roo.Component} this
16432              */
16433         beforerender : true,
16434         /**
16435          * @event render
16436          * Fires after the component is rendered.
16437              * @param {Roo.Component} this
16438              */
16439         render : true,
16440         /**
16441          * @event beforedestroy
16442          * Fires before the component is destroyed. Return false to stop the destroy.
16443              * @param {Roo.Component} this
16444              */
16445         beforedestroy : true,
16446         /**
16447          * @event destroy
16448          * Fires after the component is destroyed.
16449              * @param {Roo.Component} this
16450              */
16451         destroy : true
16452     });
16453     if(!this.id){
16454         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16455     }
16456     Roo.ComponentMgr.register(this);
16457     Roo.Component.superclass.constructor.call(this);
16458     this.initComponent();
16459     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16460         this.render(this.renderTo);
16461         delete this.renderTo;
16462     }
16463 };
16464
16465 /** @private */
16466 Roo.Component.AUTO_ID = 1000;
16467
16468 Roo.extend(Roo.Component, Roo.util.Observable, {
16469     /**
16470      * @scope Roo.Component.prototype
16471      * @type {Boolean}
16472      * true if this component is hidden. Read-only.
16473      */
16474     hidden : false,
16475     /**
16476      * @type {Boolean}
16477      * true if this component is disabled. Read-only.
16478      */
16479     disabled : false,
16480     /**
16481      * @type {Boolean}
16482      * true if this component has been rendered. Read-only.
16483      */
16484     rendered : false,
16485     
16486     /** @cfg {String} disableClass
16487      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16488      */
16489     disabledClass : "x-item-disabled",
16490         /** @cfg {Boolean} allowDomMove
16491          * Whether the component can move the Dom node when rendering (defaults to true).
16492          */
16493     allowDomMove : true,
16494     /** @cfg {String} hideMode (display|visibility)
16495      * How this component should hidden. Supported values are
16496      * "visibility" (css visibility), "offsets" (negative offset position) and
16497      * "display" (css display) - defaults to "display".
16498      */
16499     hideMode: 'display',
16500
16501     /** @private */
16502     ctype : "Roo.Component",
16503
16504     /**
16505      * @cfg {String} actionMode 
16506      * which property holds the element that used for  hide() / show() / disable() / enable()
16507      * default is 'el' for forms you probably want to set this to fieldEl 
16508      */
16509     actionMode : "el",
16510
16511     /** @private */
16512     getActionEl : function(){
16513         return this[this.actionMode];
16514     },
16515
16516     initComponent : Roo.emptyFn,
16517     /**
16518      * If this is a lazy rendering component, render it to its container element.
16519      * @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.
16520      */
16521     render : function(container, position){
16522         
16523         if(this.rendered){
16524             return this;
16525         }
16526         
16527         if(this.fireEvent("beforerender", this) === false){
16528             return false;
16529         }
16530         
16531         if(!container && this.el){
16532             this.el = Roo.get(this.el);
16533             container = this.el.dom.parentNode;
16534             this.allowDomMove = false;
16535         }
16536         this.container = Roo.get(container);
16537         this.rendered = true;
16538         if(position !== undefined){
16539             if(typeof position == 'number'){
16540                 position = this.container.dom.childNodes[position];
16541             }else{
16542                 position = Roo.getDom(position);
16543             }
16544         }
16545         this.onRender(this.container, position || null);
16546         if(this.cls){
16547             this.el.addClass(this.cls);
16548             delete this.cls;
16549         }
16550         if(this.style){
16551             this.el.applyStyles(this.style);
16552             delete this.style;
16553         }
16554         this.fireEvent("render", this);
16555         this.afterRender(this.container);
16556         if(this.hidden){
16557             this.hide();
16558         }
16559         if(this.disabled){
16560             this.disable();
16561         }
16562
16563         return this;
16564         
16565     },
16566
16567     /** @private */
16568     // default function is not really useful
16569     onRender : function(ct, position){
16570         if(this.el){
16571             this.el = Roo.get(this.el);
16572             if(this.allowDomMove !== false){
16573                 ct.dom.insertBefore(this.el.dom, position);
16574             }
16575         }
16576     },
16577
16578     /** @private */
16579     getAutoCreate : function(){
16580         var cfg = typeof this.autoCreate == "object" ?
16581                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16582         if(this.id && !cfg.id){
16583             cfg.id = this.id;
16584         }
16585         return cfg;
16586     },
16587
16588     /** @private */
16589     afterRender : Roo.emptyFn,
16590
16591     /**
16592      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16593      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16594      */
16595     destroy : function(){
16596         if(this.fireEvent("beforedestroy", this) !== false){
16597             this.purgeListeners();
16598             this.beforeDestroy();
16599             if(this.rendered){
16600                 this.el.removeAllListeners();
16601                 this.el.remove();
16602                 if(this.actionMode == "container"){
16603                     this.container.remove();
16604                 }
16605             }
16606             this.onDestroy();
16607             Roo.ComponentMgr.unregister(this);
16608             this.fireEvent("destroy", this);
16609         }
16610     },
16611
16612         /** @private */
16613     beforeDestroy : function(){
16614
16615     },
16616
16617         /** @private */
16618         onDestroy : function(){
16619
16620     },
16621
16622     /**
16623      * Returns the underlying {@link Roo.Element}.
16624      * @return {Roo.Element} The element
16625      */
16626     getEl : function(){
16627         return this.el;
16628     },
16629
16630     /**
16631      * Returns the id of this component.
16632      * @return {String}
16633      */
16634     getId : function(){
16635         return this.id;
16636     },
16637
16638     /**
16639      * Try to focus this component.
16640      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16641      * @return {Roo.Component} this
16642      */
16643     focus : function(selectText){
16644         if(this.rendered){
16645             this.el.focus();
16646             if(selectText === true){
16647                 this.el.dom.select();
16648             }
16649         }
16650         return this;
16651     },
16652
16653     /** @private */
16654     blur : function(){
16655         if(this.rendered){
16656             this.el.blur();
16657         }
16658         return this;
16659     },
16660
16661     /**
16662      * Disable this component.
16663      * @return {Roo.Component} this
16664      */
16665     disable : function(){
16666         if(this.rendered){
16667             this.onDisable();
16668         }
16669         this.disabled = true;
16670         this.fireEvent("disable", this);
16671         return this;
16672     },
16673
16674         // private
16675     onDisable : function(){
16676         this.getActionEl().addClass(this.disabledClass);
16677         this.el.dom.disabled = true;
16678     },
16679
16680     /**
16681      * Enable this component.
16682      * @return {Roo.Component} this
16683      */
16684     enable : function(){
16685         if(this.rendered){
16686             this.onEnable();
16687         }
16688         this.disabled = false;
16689         this.fireEvent("enable", this);
16690         return this;
16691     },
16692
16693         // private
16694     onEnable : function(){
16695         this.getActionEl().removeClass(this.disabledClass);
16696         this.el.dom.disabled = false;
16697     },
16698
16699     /**
16700      * Convenience function for setting disabled/enabled by boolean.
16701      * @param {Boolean} disabled
16702      */
16703     setDisabled : function(disabled){
16704         this[disabled ? "disable" : "enable"]();
16705     },
16706
16707     /**
16708      * Show this component.
16709      * @return {Roo.Component} this
16710      */
16711     show: function(){
16712         if(this.fireEvent("beforeshow", this) !== false){
16713             this.hidden = false;
16714             if(this.rendered){
16715                 this.onShow();
16716             }
16717             this.fireEvent("show", this);
16718         }
16719         return this;
16720     },
16721
16722     // private
16723     onShow : function(){
16724         var ae = this.getActionEl();
16725         if(this.hideMode == 'visibility'){
16726             ae.dom.style.visibility = "visible";
16727         }else if(this.hideMode == 'offsets'){
16728             ae.removeClass('x-hidden');
16729         }else{
16730             ae.dom.style.display = "";
16731         }
16732     },
16733
16734     /**
16735      * Hide this component.
16736      * @return {Roo.Component} this
16737      */
16738     hide: function(){
16739         if(this.fireEvent("beforehide", this) !== false){
16740             this.hidden = true;
16741             if(this.rendered){
16742                 this.onHide();
16743             }
16744             this.fireEvent("hide", this);
16745         }
16746         return this;
16747     },
16748
16749     // private
16750     onHide : function(){
16751         var ae = this.getActionEl();
16752         if(this.hideMode == 'visibility'){
16753             ae.dom.style.visibility = "hidden";
16754         }else if(this.hideMode == 'offsets'){
16755             ae.addClass('x-hidden');
16756         }else{
16757             ae.dom.style.display = "none";
16758         }
16759     },
16760
16761     /**
16762      * Convenience function to hide or show this component by boolean.
16763      * @param {Boolean} visible True to show, false to hide
16764      * @return {Roo.Component} this
16765      */
16766     setVisible: function(visible){
16767         if(visible) {
16768             this.show();
16769         }else{
16770             this.hide();
16771         }
16772         return this;
16773     },
16774
16775     /**
16776      * Returns true if this component is visible.
16777      */
16778     isVisible : function(){
16779         return this.getActionEl().isVisible();
16780     },
16781
16782     cloneConfig : function(overrides){
16783         overrides = overrides || {};
16784         var id = overrides.id || Roo.id();
16785         var cfg = Roo.applyIf(overrides, this.initialConfig);
16786         cfg.id = id; // prevent dup id
16787         return new this.constructor(cfg);
16788     }
16789 });/*
16790  * Based on:
16791  * Ext JS Library 1.1.1
16792  * Copyright(c) 2006-2007, Ext JS, LLC.
16793  *
16794  * Originally Released Under LGPL - original licence link has changed is not relivant.
16795  *
16796  * Fork - LGPL
16797  * <script type="text/javascript">
16798  */
16799
16800 /**
16801  * @class Roo.BoxComponent
16802  * @extends Roo.Component
16803  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16804  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16805  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16806  * layout containers.
16807  * @constructor
16808  * @param {Roo.Element/String/Object} config The configuration options.
16809  */
16810 Roo.BoxComponent = function(config){
16811     Roo.Component.call(this, config);
16812     this.addEvents({
16813         /**
16814          * @event resize
16815          * Fires after the component is resized.
16816              * @param {Roo.Component} this
16817              * @param {Number} adjWidth The box-adjusted width that was set
16818              * @param {Number} adjHeight The box-adjusted height that was set
16819              * @param {Number} rawWidth The width that was originally specified
16820              * @param {Number} rawHeight The height that was originally specified
16821              */
16822         resize : true,
16823         /**
16824          * @event move
16825          * Fires after the component is moved.
16826              * @param {Roo.Component} this
16827              * @param {Number} x The new x position
16828              * @param {Number} y The new y position
16829              */
16830         move : true
16831     });
16832 };
16833
16834 Roo.extend(Roo.BoxComponent, Roo.Component, {
16835     // private, set in afterRender to signify that the component has been rendered
16836     boxReady : false,
16837     // private, used to defer height settings to subclasses
16838     deferHeight: false,
16839     /** @cfg {Number} width
16840      * width (optional) size of component
16841      */
16842      /** @cfg {Number} height
16843      * height (optional) size of component
16844      */
16845      
16846     /**
16847      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16848      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16849      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16850      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16851      * @return {Roo.BoxComponent} this
16852      */
16853     setSize : function(w, h){
16854         // support for standard size objects
16855         if(typeof w == 'object'){
16856             h = w.height;
16857             w = w.width;
16858         }
16859         // not rendered
16860         if(!this.boxReady){
16861             this.width = w;
16862             this.height = h;
16863             return this;
16864         }
16865
16866         // prevent recalcs when not needed
16867         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16868             return this;
16869         }
16870         this.lastSize = {width: w, height: h};
16871
16872         var adj = this.adjustSize(w, h);
16873         var aw = adj.width, ah = adj.height;
16874         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16875             var rz = this.getResizeEl();
16876             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16877                 rz.setSize(aw, ah);
16878             }else if(!this.deferHeight && ah !== undefined){
16879                 rz.setHeight(ah);
16880             }else if(aw !== undefined){
16881                 rz.setWidth(aw);
16882             }
16883             this.onResize(aw, ah, w, h);
16884             this.fireEvent('resize', this, aw, ah, w, h);
16885         }
16886         return this;
16887     },
16888
16889     /**
16890      * Gets the current size of the component's underlying element.
16891      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16892      */
16893     getSize : function(){
16894         return this.el.getSize();
16895     },
16896
16897     /**
16898      * Gets the current XY position of the component's underlying element.
16899      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16900      * @return {Array} The XY position of the element (e.g., [100, 200])
16901      */
16902     getPosition : function(local){
16903         if(local === true){
16904             return [this.el.getLeft(true), this.el.getTop(true)];
16905         }
16906         return this.xy || this.el.getXY();
16907     },
16908
16909     /**
16910      * Gets the current box measurements of the component's underlying element.
16911      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16912      * @returns {Object} box An object in the format {x, y, width, height}
16913      */
16914     getBox : function(local){
16915         var s = this.el.getSize();
16916         if(local){
16917             s.x = this.el.getLeft(true);
16918             s.y = this.el.getTop(true);
16919         }else{
16920             var xy = this.xy || this.el.getXY();
16921             s.x = xy[0];
16922             s.y = xy[1];
16923         }
16924         return s;
16925     },
16926
16927     /**
16928      * Sets the current box measurements of the component's underlying element.
16929      * @param {Object} box An object in the format {x, y, width, height}
16930      * @returns {Roo.BoxComponent} this
16931      */
16932     updateBox : function(box){
16933         this.setSize(box.width, box.height);
16934         this.setPagePosition(box.x, box.y);
16935         return this;
16936     },
16937
16938     // protected
16939     getResizeEl : function(){
16940         return this.resizeEl || this.el;
16941     },
16942
16943     // protected
16944     getPositionEl : function(){
16945         return this.positionEl || this.el;
16946     },
16947
16948     /**
16949      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16950      * This method fires the move event.
16951      * @param {Number} left The new left
16952      * @param {Number} top The new top
16953      * @returns {Roo.BoxComponent} this
16954      */
16955     setPosition : function(x, y){
16956         this.x = x;
16957         this.y = y;
16958         if(!this.boxReady){
16959             return this;
16960         }
16961         var adj = this.adjustPosition(x, y);
16962         var ax = adj.x, ay = adj.y;
16963
16964         var el = this.getPositionEl();
16965         if(ax !== undefined || ay !== undefined){
16966             if(ax !== undefined && ay !== undefined){
16967                 el.setLeftTop(ax, ay);
16968             }else if(ax !== undefined){
16969                 el.setLeft(ax);
16970             }else if(ay !== undefined){
16971                 el.setTop(ay);
16972             }
16973             this.onPosition(ax, ay);
16974             this.fireEvent('move', this, ax, ay);
16975         }
16976         return this;
16977     },
16978
16979     /**
16980      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16981      * This method fires the move event.
16982      * @param {Number} x The new x position
16983      * @param {Number} y The new y position
16984      * @returns {Roo.BoxComponent} this
16985      */
16986     setPagePosition : function(x, y){
16987         this.pageX = x;
16988         this.pageY = y;
16989         if(!this.boxReady){
16990             return;
16991         }
16992         if(x === undefined || y === undefined){ // cannot translate undefined points
16993             return;
16994         }
16995         var p = this.el.translatePoints(x, y);
16996         this.setPosition(p.left, p.top);
16997         return this;
16998     },
16999
17000     // private
17001     onRender : function(ct, position){
17002         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17003         if(this.resizeEl){
17004             this.resizeEl = Roo.get(this.resizeEl);
17005         }
17006         if(this.positionEl){
17007             this.positionEl = Roo.get(this.positionEl);
17008         }
17009     },
17010
17011     // private
17012     afterRender : function(){
17013         Roo.BoxComponent.superclass.afterRender.call(this);
17014         this.boxReady = true;
17015         this.setSize(this.width, this.height);
17016         if(this.x || this.y){
17017             this.setPosition(this.x, this.y);
17018         }
17019         if(this.pageX || this.pageY){
17020             this.setPagePosition(this.pageX, this.pageY);
17021         }
17022     },
17023
17024     /**
17025      * Force the component's size to recalculate based on the underlying element's current height and width.
17026      * @returns {Roo.BoxComponent} this
17027      */
17028     syncSize : function(){
17029         delete this.lastSize;
17030         this.setSize(this.el.getWidth(), this.el.getHeight());
17031         return this;
17032     },
17033
17034     /**
17035      * Called after the component is resized, this method is empty by default but can be implemented by any
17036      * subclass that needs to perform custom logic after a resize occurs.
17037      * @param {Number} adjWidth The box-adjusted width that was set
17038      * @param {Number} adjHeight The box-adjusted height that was set
17039      * @param {Number} rawWidth The width that was originally specified
17040      * @param {Number} rawHeight The height that was originally specified
17041      */
17042     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17043
17044     },
17045
17046     /**
17047      * Called after the component is moved, this method is empty by default but can be implemented by any
17048      * subclass that needs to perform custom logic after a move occurs.
17049      * @param {Number} x The new x position
17050      * @param {Number} y The new y position
17051      */
17052     onPosition : function(x, y){
17053
17054     },
17055
17056     // private
17057     adjustSize : function(w, h){
17058         if(this.autoWidth){
17059             w = 'auto';
17060         }
17061         if(this.autoHeight){
17062             h = 'auto';
17063         }
17064         return {width : w, height: h};
17065     },
17066
17067     // private
17068     adjustPosition : function(x, y){
17069         return {x : x, y: y};
17070     }
17071 });/*
17072  * Based on:
17073  * Ext JS Library 1.1.1
17074  * Copyright(c) 2006-2007, Ext JS, LLC.
17075  *
17076  * Originally Released Under LGPL - original licence link has changed is not relivant.
17077  *
17078  * Fork - LGPL
17079  * <script type="text/javascript">
17080  */
17081  (function(){ 
17082 /**
17083  * @class Roo.Layer
17084  * @extends Roo.Element
17085  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17086  * automatic maintaining of shadow/shim positions.
17087  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17088  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17089  * you can pass a string with a CSS class name. False turns off the shadow.
17090  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17091  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17092  * @cfg {String} cls CSS class to add to the element
17093  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17094  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17095  * @constructor
17096  * @param {Object} config An object with config options.
17097  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17098  */
17099
17100 Roo.Layer = function(config, existingEl){
17101     config = config || {};
17102     var dh = Roo.DomHelper;
17103     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17104     if(existingEl){
17105         this.dom = Roo.getDom(existingEl);
17106     }
17107     if(!this.dom){
17108         var o = config.dh || {tag: "div", cls: "x-layer"};
17109         this.dom = dh.append(pel, o);
17110     }
17111     if(config.cls){
17112         this.addClass(config.cls);
17113     }
17114     this.constrain = config.constrain !== false;
17115     this.visibilityMode = Roo.Element.VISIBILITY;
17116     if(config.id){
17117         this.id = this.dom.id = config.id;
17118     }else{
17119         this.id = Roo.id(this.dom);
17120     }
17121     this.zindex = config.zindex || this.getZIndex();
17122     this.position("absolute", this.zindex);
17123     if(config.shadow){
17124         this.shadowOffset = config.shadowOffset || 4;
17125         this.shadow = new Roo.Shadow({
17126             offset : this.shadowOffset,
17127             mode : config.shadow
17128         });
17129     }else{
17130         this.shadowOffset = 0;
17131     }
17132     this.useShim = config.shim !== false && Roo.useShims;
17133     this.useDisplay = config.useDisplay;
17134     this.hide();
17135 };
17136
17137 var supr = Roo.Element.prototype;
17138
17139 // shims are shared among layer to keep from having 100 iframes
17140 var shims = [];
17141
17142 Roo.extend(Roo.Layer, Roo.Element, {
17143
17144     getZIndex : function(){
17145         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17146     },
17147
17148     getShim : function(){
17149         if(!this.useShim){
17150             return null;
17151         }
17152         if(this.shim){
17153             return this.shim;
17154         }
17155         var shim = shims.shift();
17156         if(!shim){
17157             shim = this.createShim();
17158             shim.enableDisplayMode('block');
17159             shim.dom.style.display = 'none';
17160             shim.dom.style.visibility = 'visible';
17161         }
17162         var pn = this.dom.parentNode;
17163         if(shim.dom.parentNode != pn){
17164             pn.insertBefore(shim.dom, this.dom);
17165         }
17166         shim.setStyle('z-index', this.getZIndex()-2);
17167         this.shim = shim;
17168         return shim;
17169     },
17170
17171     hideShim : function(){
17172         if(this.shim){
17173             this.shim.setDisplayed(false);
17174             shims.push(this.shim);
17175             delete this.shim;
17176         }
17177     },
17178
17179     disableShadow : function(){
17180         if(this.shadow){
17181             this.shadowDisabled = true;
17182             this.shadow.hide();
17183             this.lastShadowOffset = this.shadowOffset;
17184             this.shadowOffset = 0;
17185         }
17186     },
17187
17188     enableShadow : function(show){
17189         if(this.shadow){
17190             this.shadowDisabled = false;
17191             this.shadowOffset = this.lastShadowOffset;
17192             delete this.lastShadowOffset;
17193             if(show){
17194                 this.sync(true);
17195             }
17196         }
17197     },
17198
17199     // private
17200     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17201     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17202     sync : function(doShow){
17203         var sw = this.shadow;
17204         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17205             var sh = this.getShim();
17206
17207             var w = this.getWidth(),
17208                 h = this.getHeight();
17209
17210             var l = this.getLeft(true),
17211                 t = this.getTop(true);
17212
17213             if(sw && !this.shadowDisabled){
17214                 if(doShow && !sw.isVisible()){
17215                     sw.show(this);
17216                 }else{
17217                     sw.realign(l, t, w, h);
17218                 }
17219                 if(sh){
17220                     if(doShow){
17221                        sh.show();
17222                     }
17223                     // fit the shim behind the shadow, so it is shimmed too
17224                     var a = sw.adjusts, s = sh.dom.style;
17225                     s.left = (Math.min(l, l+a.l))+"px";
17226                     s.top = (Math.min(t, t+a.t))+"px";
17227                     s.width = (w+a.w)+"px";
17228                     s.height = (h+a.h)+"px";
17229                 }
17230             }else if(sh){
17231                 if(doShow){
17232                    sh.show();
17233                 }
17234                 sh.setSize(w, h);
17235                 sh.setLeftTop(l, t);
17236             }
17237             
17238         }
17239     },
17240
17241     // private
17242     destroy : function(){
17243         this.hideShim();
17244         if(this.shadow){
17245             this.shadow.hide();
17246         }
17247         this.removeAllListeners();
17248         var pn = this.dom.parentNode;
17249         if(pn){
17250             pn.removeChild(this.dom);
17251         }
17252         Roo.Element.uncache(this.id);
17253     },
17254
17255     remove : function(){
17256         this.destroy();
17257     },
17258
17259     // private
17260     beginUpdate : function(){
17261         this.updating = true;
17262     },
17263
17264     // private
17265     endUpdate : function(){
17266         this.updating = false;
17267         this.sync(true);
17268     },
17269
17270     // private
17271     hideUnders : function(negOffset){
17272         if(this.shadow){
17273             this.shadow.hide();
17274         }
17275         this.hideShim();
17276     },
17277
17278     // private
17279     constrainXY : function(){
17280         if(this.constrain){
17281             var vw = Roo.lib.Dom.getViewWidth(),
17282                 vh = Roo.lib.Dom.getViewHeight();
17283             var s = Roo.get(document).getScroll();
17284
17285             var xy = this.getXY();
17286             var x = xy[0], y = xy[1];   
17287             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17288             // only move it if it needs it
17289             var moved = false;
17290             // first validate right/bottom
17291             if((x + w) > vw+s.left){
17292                 x = vw - w - this.shadowOffset;
17293                 moved = true;
17294             }
17295             if((y + h) > vh+s.top){
17296                 y = vh - h - this.shadowOffset;
17297                 moved = true;
17298             }
17299             // then make sure top/left isn't negative
17300             if(x < s.left){
17301                 x = s.left;
17302                 moved = true;
17303             }
17304             if(y < s.top){
17305                 y = s.top;
17306                 moved = true;
17307             }
17308             if(moved){
17309                 if(this.avoidY){
17310                     var ay = this.avoidY;
17311                     if(y <= ay && (y+h) >= ay){
17312                         y = ay-h-5;   
17313                     }
17314                 }
17315                 xy = [x, y];
17316                 this.storeXY(xy);
17317                 supr.setXY.call(this, xy);
17318                 this.sync();
17319             }
17320         }
17321     },
17322
17323     isVisible : function(){
17324         return this.visible;    
17325     },
17326
17327     // private
17328     showAction : function(){
17329         this.visible = true; // track visibility to prevent getStyle calls
17330         if(this.useDisplay === true){
17331             this.setDisplayed("");
17332         }else if(this.lastXY){
17333             supr.setXY.call(this, this.lastXY);
17334         }else if(this.lastLT){
17335             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17336         }
17337     },
17338
17339     // private
17340     hideAction : function(){
17341         this.visible = false;
17342         if(this.useDisplay === true){
17343             this.setDisplayed(false);
17344         }else{
17345             this.setLeftTop(-10000,-10000);
17346         }
17347     },
17348
17349     // overridden Element method
17350     setVisible : function(v, a, d, c, e){
17351         if(v){
17352             this.showAction();
17353         }
17354         if(a && v){
17355             var cb = function(){
17356                 this.sync(true);
17357                 if(c){
17358                     c();
17359                 }
17360             }.createDelegate(this);
17361             supr.setVisible.call(this, true, true, d, cb, e);
17362         }else{
17363             if(!v){
17364                 this.hideUnders(true);
17365             }
17366             var cb = c;
17367             if(a){
17368                 cb = function(){
17369                     this.hideAction();
17370                     if(c){
17371                         c();
17372                     }
17373                 }.createDelegate(this);
17374             }
17375             supr.setVisible.call(this, v, a, d, cb, e);
17376             if(v){
17377                 this.sync(true);
17378             }else if(!a){
17379                 this.hideAction();
17380             }
17381         }
17382     },
17383
17384     storeXY : function(xy){
17385         delete this.lastLT;
17386         this.lastXY = xy;
17387     },
17388
17389     storeLeftTop : function(left, top){
17390         delete this.lastXY;
17391         this.lastLT = [left, top];
17392     },
17393
17394     // private
17395     beforeFx : function(){
17396         this.beforeAction();
17397         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17398     },
17399
17400     // private
17401     afterFx : function(){
17402         Roo.Layer.superclass.afterFx.apply(this, arguments);
17403         this.sync(this.isVisible());
17404     },
17405
17406     // private
17407     beforeAction : function(){
17408         if(!this.updating && this.shadow){
17409             this.shadow.hide();
17410         }
17411     },
17412
17413     // overridden Element method
17414     setLeft : function(left){
17415         this.storeLeftTop(left, this.getTop(true));
17416         supr.setLeft.apply(this, arguments);
17417         this.sync();
17418     },
17419
17420     setTop : function(top){
17421         this.storeLeftTop(this.getLeft(true), top);
17422         supr.setTop.apply(this, arguments);
17423         this.sync();
17424     },
17425
17426     setLeftTop : function(left, top){
17427         this.storeLeftTop(left, top);
17428         supr.setLeftTop.apply(this, arguments);
17429         this.sync();
17430     },
17431
17432     setXY : function(xy, a, d, c, e){
17433         this.fixDisplay();
17434         this.beforeAction();
17435         this.storeXY(xy);
17436         var cb = this.createCB(c);
17437         supr.setXY.call(this, xy, a, d, cb, e);
17438         if(!a){
17439             cb();
17440         }
17441     },
17442
17443     // private
17444     createCB : function(c){
17445         var el = this;
17446         return function(){
17447             el.constrainXY();
17448             el.sync(true);
17449             if(c){
17450                 c();
17451             }
17452         };
17453     },
17454
17455     // overridden Element method
17456     setX : function(x, a, d, c, e){
17457         this.setXY([x, this.getY()], a, d, c, e);
17458     },
17459
17460     // overridden Element method
17461     setY : function(y, a, d, c, e){
17462         this.setXY([this.getX(), y], a, d, c, e);
17463     },
17464
17465     // overridden Element method
17466     setSize : function(w, h, a, d, c, e){
17467         this.beforeAction();
17468         var cb = this.createCB(c);
17469         supr.setSize.call(this, w, h, a, d, cb, e);
17470         if(!a){
17471             cb();
17472         }
17473     },
17474
17475     // overridden Element method
17476     setWidth : function(w, a, d, c, e){
17477         this.beforeAction();
17478         var cb = this.createCB(c);
17479         supr.setWidth.call(this, w, a, d, cb, e);
17480         if(!a){
17481             cb();
17482         }
17483     },
17484
17485     // overridden Element method
17486     setHeight : function(h, a, d, c, e){
17487         this.beforeAction();
17488         var cb = this.createCB(c);
17489         supr.setHeight.call(this, h, a, d, cb, e);
17490         if(!a){
17491             cb();
17492         }
17493     },
17494
17495     // overridden Element method
17496     setBounds : function(x, y, w, h, a, d, c, e){
17497         this.beforeAction();
17498         var cb = this.createCB(c);
17499         if(!a){
17500             this.storeXY([x, y]);
17501             supr.setXY.call(this, [x, y]);
17502             supr.setSize.call(this, w, h, a, d, cb, e);
17503             cb();
17504         }else{
17505             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17506         }
17507         return this;
17508     },
17509     
17510     /**
17511      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17512      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17513      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17514      * @param {Number} zindex The new z-index to set
17515      * @return {this} The Layer
17516      */
17517     setZIndex : function(zindex){
17518         this.zindex = zindex;
17519         this.setStyle("z-index", zindex + 2);
17520         if(this.shadow){
17521             this.shadow.setZIndex(zindex + 1);
17522         }
17523         if(this.shim){
17524             this.shim.setStyle("z-index", zindex);
17525         }
17526     }
17527 });
17528 })();/*
17529  * Original code for Roojs - LGPL
17530  * <script type="text/javascript">
17531  */
17532  
17533 /**
17534  * @class Roo.XComponent
17535  * A delayed Element creator...
17536  * Or a way to group chunks of interface together.
17537  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17538  *  used in conjunction with XComponent.build() it will create an instance of each element,
17539  *  then call addxtype() to build the User interface.
17540  * 
17541  * Mypart.xyx = new Roo.XComponent({
17542
17543     parent : 'Mypart.xyz', // empty == document.element.!!
17544     order : '001',
17545     name : 'xxxx'
17546     region : 'xxxx'
17547     disabled : function() {} 
17548      
17549     tree : function() { // return an tree of xtype declared components
17550         var MODULE = this;
17551         return 
17552         {
17553             xtype : 'NestedLayoutPanel',
17554             // technicall
17555         }
17556      ]
17557  *})
17558  *
17559  *
17560  * It can be used to build a big heiracy, with parent etc.
17561  * or you can just use this to render a single compoent to a dom element
17562  * MYPART.render(Roo.Element | String(id) | dom_element )
17563  *
17564  *
17565  * Usage patterns.
17566  *
17567  * Classic Roo
17568  *
17569  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17570  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17571  *
17572  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17573  *
17574  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17575  * - if mulitple topModules exist, the last one is defined as the top module.
17576  *
17577  * Embeded Roo
17578  * 
17579  * When the top level or multiple modules are to embedded into a existing HTML page,
17580  * the parent element can container '#id' of the element where the module will be drawn.
17581  *
17582  * Bootstrap Roo
17583  *
17584  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17585  * it relies more on a include mechanism, where sub modules are included into an outer page.
17586  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17587  * 
17588  * Bootstrap Roo Included elements
17589  *
17590  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17591  * hence confusing the component builder as it thinks there are multiple top level elements. 
17592  *
17593  * String Over-ride & Translations
17594  *
17595  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17596  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17597  * are needed. @see Roo.XComponent.overlayString  
17598  * 
17599  * 
17600  * 
17601  * @extends Roo.util.Observable
17602  * @constructor
17603  * @param cfg {Object} configuration of component
17604  * 
17605  */
17606 Roo.XComponent = function(cfg) {
17607     Roo.apply(this, cfg);
17608     this.addEvents({ 
17609         /**
17610              * @event built
17611              * Fires when this the componnt is built
17612              * @param {Roo.XComponent} c the component
17613              */
17614         'built' : true
17615         
17616     });
17617     this.region = this.region || 'center'; // default..
17618     Roo.XComponent.register(this);
17619     this.modules = false;
17620     this.el = false; // where the layout goes..
17621     
17622     
17623 }
17624 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17625     /**
17626      * @property el
17627      * The created element (with Roo.factory())
17628      * @type {Roo.Layout}
17629      */
17630     el  : false,
17631     
17632     /**
17633      * @property el
17634      * for BC  - use el in new code
17635      * @type {Roo.Layout}
17636      */
17637     panel : false,
17638     
17639     /**
17640      * @property layout
17641      * for BC  - use el in new code
17642      * @type {Roo.Layout}
17643      */
17644     layout : false,
17645     
17646      /**
17647      * @cfg {Function|boolean} disabled
17648      * If this module is disabled by some rule, return true from the funtion
17649      */
17650     disabled : false,
17651     
17652     /**
17653      * @cfg {String} parent 
17654      * Name of parent element which it get xtype added to..
17655      */
17656     parent: false,
17657     
17658     /**
17659      * @cfg {String} order
17660      * Used to set the order in which elements are created (usefull for multiple tabs)
17661      */
17662     
17663     order : false,
17664     /**
17665      * @cfg {String} name
17666      * String to display while loading.
17667      */
17668     name : false,
17669     /**
17670      * @cfg {String} region
17671      * Region to render component to (defaults to center)
17672      */
17673     region : 'center',
17674     
17675     /**
17676      * @cfg {Array} items
17677      * A single item array - the first element is the root of the tree..
17678      * It's done this way to stay compatible with the Xtype system...
17679      */
17680     items : false,
17681     
17682     /**
17683      * @property _tree
17684      * The method that retuns the tree of parts that make up this compoennt 
17685      * @type {function}
17686      */
17687     _tree  : false,
17688     
17689      /**
17690      * render
17691      * render element to dom or tree
17692      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17693      */
17694     
17695     render : function(el)
17696     {
17697         
17698         el = el || false;
17699         var hp = this.parent ? 1 : 0;
17700         Roo.debug &&  Roo.log(this);
17701         
17702         var tree = this._tree ? this._tree() : this.tree();
17703
17704         
17705         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17706             // if parent is a '#.....' string, then let's use that..
17707             var ename = this.parent.substr(1);
17708             this.parent = false;
17709             Roo.debug && Roo.log(ename);
17710             switch (ename) {
17711                 case 'bootstrap-body':
17712                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17713                         // this is the BorderLayout standard?
17714                        this.parent = { el : true };
17715                        break;
17716                     }
17717                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17718                         // need to insert stuff...
17719                         this.parent =  {
17720                              el : new Roo.bootstrap.layout.Border({
17721                                  el : document.body, 
17722                      
17723                                  center: {
17724                                     titlebar: false,
17725                                     autoScroll:false,
17726                                     closeOnTab: true,
17727                                     tabPosition: 'top',
17728                                       //resizeTabs: true,
17729                                     alwaysShowTabs: true,
17730                                     hideTabs: false
17731                                      //minTabWidth: 140
17732                                  }
17733                              })
17734                         
17735                          };
17736                          break;
17737                     }
17738                          
17739                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17740                         this.parent = { el :  new  Roo.bootstrap.Body() };
17741                         Roo.debug && Roo.log("setting el to doc body");
17742                          
17743                     } else {
17744                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17745                     }
17746                     break;
17747                 case 'bootstrap':
17748                     this.parent = { el : true};
17749                     // fall through
17750                 default:
17751                     el = Roo.get(ename);
17752                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17753                         this.parent = { el : true};
17754                     }
17755                     
17756                     break;
17757             }
17758                 
17759             
17760             if (!el && !this.parent) {
17761                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17762                 return;
17763             }
17764         }
17765         
17766         Roo.debug && Roo.log("EL:");
17767         Roo.debug && Roo.log(el);
17768         Roo.debug && Roo.log("this.parent.el:");
17769         Roo.debug && Roo.log(this.parent.el);
17770         
17771
17772         // altertive root elements ??? - we need a better way to indicate these.
17773         var is_alt = Roo.XComponent.is_alt ||
17774                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17775                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17776                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17777         
17778         
17779         
17780         if (!this.parent && is_alt) {
17781             //el = Roo.get(document.body);
17782             this.parent = { el : true };
17783         }
17784             
17785             
17786         
17787         if (!this.parent) {
17788             
17789             Roo.debug && Roo.log("no parent - creating one");
17790             
17791             el = el ? Roo.get(el) : false;      
17792             
17793             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17794                 
17795                 this.parent =  {
17796                     el : new Roo.bootstrap.layout.Border({
17797                         el: el || document.body,
17798                     
17799                         center: {
17800                             titlebar: false,
17801                             autoScroll:false,
17802                             closeOnTab: true,
17803                             tabPosition: 'top',
17804                              //resizeTabs: true,
17805                             alwaysShowTabs: false,
17806                             hideTabs: true,
17807                             minTabWidth: 140,
17808                             overflow: 'visible'
17809                          }
17810                      })
17811                 };
17812             } else {
17813             
17814                 // it's a top level one..
17815                 this.parent =  {
17816                     el : new Roo.BorderLayout(el || document.body, {
17817                         center: {
17818                             titlebar: false,
17819                             autoScroll:false,
17820                             closeOnTab: true,
17821                             tabPosition: 'top',
17822                              //resizeTabs: true,
17823                             alwaysShowTabs: el && hp? false :  true,
17824                             hideTabs: el || !hp ? true :  false,
17825                             minTabWidth: 140
17826                          }
17827                     })
17828                 };
17829             }
17830         }
17831         
17832         if (!this.parent.el) {
17833                 // probably an old style ctor, which has been disabled.
17834                 return;
17835
17836         }
17837                 // The 'tree' method is  '_tree now' 
17838             
17839         tree.region = tree.region || this.region;
17840         var is_body = false;
17841         if (this.parent.el === true) {
17842             // bootstrap... - body..
17843             if (el) {
17844                 tree.el = el;
17845             }
17846             this.parent.el = Roo.factory(tree);
17847             is_body = true;
17848         }
17849         
17850         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17851         this.fireEvent('built', this);
17852         
17853         this.panel = this.el;
17854         this.layout = this.panel.layout;
17855         this.parentLayout = this.parent.layout  || false;  
17856          
17857     }
17858     
17859 });
17860
17861 Roo.apply(Roo.XComponent, {
17862     /**
17863      * @property  hideProgress
17864      * true to disable the building progress bar.. usefull on single page renders.
17865      * @type Boolean
17866      */
17867     hideProgress : false,
17868     /**
17869      * @property  buildCompleted
17870      * True when the builder has completed building the interface.
17871      * @type Boolean
17872      */
17873     buildCompleted : false,
17874      
17875     /**
17876      * @property  topModule
17877      * the upper most module - uses document.element as it's constructor.
17878      * @type Object
17879      */
17880      
17881     topModule  : false,
17882       
17883     /**
17884      * @property  modules
17885      * array of modules to be created by registration system.
17886      * @type {Array} of Roo.XComponent
17887      */
17888     
17889     modules : [],
17890     /**
17891      * @property  elmodules
17892      * array of modules to be created by which use #ID 
17893      * @type {Array} of Roo.XComponent
17894      */
17895      
17896     elmodules : [],
17897
17898      /**
17899      * @property  is_alt
17900      * Is an alternative Root - normally used by bootstrap or other systems,
17901      *    where the top element in the tree can wrap 'body' 
17902      * @type {boolean}  (default false)
17903      */
17904      
17905     is_alt : false,
17906     /**
17907      * @property  build_from_html
17908      * Build elements from html - used by bootstrap HTML stuff 
17909      *    - this is cleared after build is completed
17910      * @type {boolean}    (default false)
17911      */
17912      
17913     build_from_html : false,
17914     /**
17915      * Register components to be built later.
17916      *
17917      * This solves the following issues
17918      * - Building is not done on page load, but after an authentication process has occured.
17919      * - Interface elements are registered on page load
17920      * - Parent Interface elements may not be loaded before child, so this handles that..
17921      * 
17922      *
17923      * example:
17924      * 
17925      * MyApp.register({
17926           order : '000001',
17927           module : 'Pman.Tab.projectMgr',
17928           region : 'center',
17929           parent : 'Pman.layout',
17930           disabled : false,  // or use a function..
17931         })
17932      
17933      * * @param {Object} details about module
17934      */
17935     register : function(obj) {
17936                 
17937         Roo.XComponent.event.fireEvent('register', obj);
17938         switch(typeof(obj.disabled) ) {
17939                 
17940             case 'undefined':
17941                 break;
17942             
17943             case 'function':
17944                 if ( obj.disabled() ) {
17945                         return;
17946                 }
17947                 break;
17948             
17949             default:
17950                 if (obj.disabled || obj.region == '#disabled') {
17951                         return;
17952                 }
17953                 break;
17954         }
17955                 
17956         this.modules.push(obj);
17957          
17958     },
17959     /**
17960      * convert a string to an object..
17961      * eg. 'AAA.BBB' -> finds AAA.BBB
17962
17963      */
17964     
17965     toObject : function(str)
17966     {
17967         if (!str || typeof(str) == 'object') {
17968             return str;
17969         }
17970         if (str.substring(0,1) == '#') {
17971             return str;
17972         }
17973
17974         var ar = str.split('.');
17975         var rt, o;
17976         rt = ar.shift();
17977             /** eval:var:o */
17978         try {
17979             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17980         } catch (e) {
17981             throw "Module not found : " + str;
17982         }
17983         
17984         if (o === false) {
17985             throw "Module not found : " + str;
17986         }
17987         Roo.each(ar, function(e) {
17988             if (typeof(o[e]) == 'undefined') {
17989                 throw "Module not found : " + str;
17990             }
17991             o = o[e];
17992         });
17993         
17994         return o;
17995         
17996     },
17997     
17998     
17999     /**
18000      * move modules into their correct place in the tree..
18001      * 
18002      */
18003     preBuild : function ()
18004     {
18005         var _t = this;
18006         Roo.each(this.modules , function (obj)
18007         {
18008             Roo.XComponent.event.fireEvent('beforebuild', obj);
18009             
18010             var opar = obj.parent;
18011             try { 
18012                 obj.parent = this.toObject(opar);
18013             } catch(e) {
18014                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18015                 return;
18016             }
18017             
18018             if (!obj.parent) {
18019                 Roo.debug && Roo.log("GOT top level module");
18020                 Roo.debug && Roo.log(obj);
18021                 obj.modules = new Roo.util.MixedCollection(false, 
18022                     function(o) { return o.order + '' }
18023                 );
18024                 this.topModule = obj;
18025                 return;
18026             }
18027                         // parent is a string (usually a dom element name..)
18028             if (typeof(obj.parent) == 'string') {
18029                 this.elmodules.push(obj);
18030                 return;
18031             }
18032             if (obj.parent.constructor != Roo.XComponent) {
18033                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18034             }
18035             if (!obj.parent.modules) {
18036                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18037                     function(o) { return o.order + '' }
18038                 );
18039             }
18040             if (obj.parent.disabled) {
18041                 obj.disabled = true;
18042             }
18043             obj.parent.modules.add(obj);
18044         }, this);
18045     },
18046     
18047      /**
18048      * make a list of modules to build.
18049      * @return {Array} list of modules. 
18050      */ 
18051     
18052     buildOrder : function()
18053     {
18054         var _this = this;
18055         var cmp = function(a,b) {   
18056             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18057         };
18058         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18059             throw "No top level modules to build";
18060         }
18061         
18062         // make a flat list in order of modules to build.
18063         var mods = this.topModule ? [ this.topModule ] : [];
18064                 
18065         
18066         // elmodules (is a list of DOM based modules )
18067         Roo.each(this.elmodules, function(e) {
18068             mods.push(e);
18069             if (!this.topModule &&
18070                 typeof(e.parent) == 'string' &&
18071                 e.parent.substring(0,1) == '#' &&
18072                 Roo.get(e.parent.substr(1))
18073                ) {
18074                 
18075                 _this.topModule = e;
18076             }
18077             
18078         });
18079
18080         
18081         // add modules to their parents..
18082         var addMod = function(m) {
18083             Roo.debug && Roo.log("build Order: add: " + m.name);
18084                 
18085             mods.push(m);
18086             if (m.modules && !m.disabled) {
18087                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18088                 m.modules.keySort('ASC',  cmp );
18089                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18090     
18091                 m.modules.each(addMod);
18092             } else {
18093                 Roo.debug && Roo.log("build Order: no child modules");
18094             }
18095             // not sure if this is used any more..
18096             if (m.finalize) {
18097                 m.finalize.name = m.name + " (clean up) ";
18098                 mods.push(m.finalize);
18099             }
18100             
18101         }
18102         if (this.topModule && this.topModule.modules) { 
18103             this.topModule.modules.keySort('ASC',  cmp );
18104             this.topModule.modules.each(addMod);
18105         } 
18106         return mods;
18107     },
18108     
18109      /**
18110      * Build the registered modules.
18111      * @param {Object} parent element.
18112      * @param {Function} optional method to call after module has been added.
18113      * 
18114      */ 
18115    
18116     build : function(opts) 
18117     {
18118         
18119         if (typeof(opts) != 'undefined') {
18120             Roo.apply(this,opts);
18121         }
18122         
18123         this.preBuild();
18124         var mods = this.buildOrder();
18125       
18126         //this.allmods = mods;
18127         //Roo.debug && Roo.log(mods);
18128         //return;
18129         if (!mods.length) { // should not happen
18130             throw "NO modules!!!";
18131         }
18132         
18133         
18134         var msg = "Building Interface...";
18135         // flash it up as modal - so we store the mask!?
18136         if (!this.hideProgress && Roo.MessageBox) {
18137             Roo.MessageBox.show({ title: 'loading' });
18138             Roo.MessageBox.show({
18139                title: "Please wait...",
18140                msg: msg,
18141                width:450,
18142                progress:true,
18143                buttons : false,
18144                closable:false,
18145                modal: false
18146               
18147             });
18148         }
18149         var total = mods.length;
18150         
18151         var _this = this;
18152         var progressRun = function() {
18153             if (!mods.length) {
18154                 Roo.debug && Roo.log('hide?');
18155                 if (!this.hideProgress && Roo.MessageBox) {
18156                     Roo.MessageBox.hide();
18157                 }
18158                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18159                 
18160                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18161                 
18162                 // THE END...
18163                 return false;   
18164             }
18165             
18166             var m = mods.shift();
18167             
18168             
18169             Roo.debug && Roo.log(m);
18170             // not sure if this is supported any more.. - modules that are are just function
18171             if (typeof(m) == 'function') { 
18172                 m.call(this);
18173                 return progressRun.defer(10, _this);
18174             } 
18175             
18176             
18177             msg = "Building Interface " + (total  - mods.length) + 
18178                     " of " + total + 
18179                     (m.name ? (' - ' + m.name) : '');
18180                         Roo.debug && Roo.log(msg);
18181             if (!_this.hideProgress &&  Roo.MessageBox) { 
18182                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18183             }
18184             
18185          
18186             // is the module disabled?
18187             var disabled = (typeof(m.disabled) == 'function') ?
18188                 m.disabled.call(m.module.disabled) : m.disabled;    
18189             
18190             
18191             if (disabled) {
18192                 return progressRun(); // we do not update the display!
18193             }
18194             
18195             // now build 
18196             
18197                         
18198                         
18199             m.render();
18200             // it's 10 on top level, and 1 on others??? why...
18201             return progressRun.defer(10, _this);
18202              
18203         }
18204         progressRun.defer(1, _this);
18205      
18206         
18207         
18208     },
18209     /**
18210      * Overlay a set of modified strings onto a component
18211      * This is dependant on our builder exporting the strings and 'named strings' elements.
18212      * 
18213      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18214      * @param {Object} associative array of 'named' string and it's new value.
18215      * 
18216      */
18217         overlayStrings : function( component, strings )
18218     {
18219         if (typeof(component['_named_strings']) == 'undefined') {
18220             throw "ERROR: component does not have _named_strings";
18221         }
18222         for ( var k in strings ) {
18223             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18224             if (md !== false) {
18225                 component['_strings'][md] = strings[k];
18226             } else {
18227                 Roo.log('could not find named string: ' + k + ' in');
18228                 Roo.log(component);
18229             }
18230             
18231         }
18232         
18233     },
18234     
18235         
18236         /**
18237          * Event Object.
18238          *
18239          *
18240          */
18241         event: false, 
18242     /**
18243          * wrapper for event.on - aliased later..  
18244          * Typically use to register a event handler for register:
18245          *
18246          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18247          *
18248          */
18249     on : false
18250    
18251     
18252     
18253 });
18254
18255 Roo.XComponent.event = new Roo.util.Observable({
18256                 events : { 
18257                         /**
18258                          * @event register
18259                          * Fires when an Component is registered,
18260                          * set the disable property on the Component to stop registration.
18261                          * @param {Roo.XComponent} c the component being registerd.
18262                          * 
18263                          */
18264                         'register' : true,
18265             /**
18266                          * @event beforebuild
18267                          * Fires before each Component is built
18268                          * can be used to apply permissions.
18269                          * @param {Roo.XComponent} c the component being registerd.
18270                          * 
18271                          */
18272                         'beforebuild' : true,
18273                         /**
18274                          * @event buildcomplete
18275                          * Fires on the top level element when all elements have been built
18276                          * @param {Roo.XComponent} the top level component.
18277                          */
18278                         'buildcomplete' : true
18279                         
18280                 }
18281 });
18282
18283 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18284  //
18285  /**
18286  * marked - a markdown parser
18287  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18288  * https://github.com/chjj/marked
18289  */
18290
18291
18292 /**
18293  *
18294  * Roo.Markdown - is a very crude wrapper around marked..
18295  *
18296  * usage:
18297  * 
18298  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18299  * 
18300  * Note: move the sample code to the bottom of this
18301  * file before uncommenting it.
18302  *
18303  */
18304
18305 Roo.Markdown = {};
18306 Roo.Markdown.toHtml = function(text) {
18307     
18308     var c = new Roo.Markdown.marked.setOptions({
18309             renderer: new Roo.Markdown.marked.Renderer(),
18310             gfm: true,
18311             tables: true,
18312             breaks: false,
18313             pedantic: false,
18314             sanitize: false,
18315             smartLists: true,
18316             smartypants: false
18317           });
18318     // A FEW HACKS!!?
18319     
18320     text = text.replace(/\\\n/g,' ');
18321     return Roo.Markdown.marked(text);
18322 };
18323 //
18324 // converter
18325 //
18326 // Wraps all "globals" so that the only thing
18327 // exposed is makeHtml().
18328 //
18329 (function() {
18330     
18331      /**
18332          * eval:var:escape
18333          * eval:var:unescape
18334          * eval:var:replace
18335          */
18336       
18337     /**
18338      * Helpers
18339      */
18340     
18341     var escape = function (html, encode) {
18342       return html
18343         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18344         .replace(/</g, '&lt;')
18345         .replace(/>/g, '&gt;')
18346         .replace(/"/g, '&quot;')
18347         .replace(/'/g, '&#39;');
18348     }
18349     
18350     var unescape = function (html) {
18351         // explicitly match decimal, hex, and named HTML entities 
18352       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18353         n = n.toLowerCase();
18354         if (n === 'colon') { return ':'; }
18355         if (n.charAt(0) === '#') {
18356           return n.charAt(1) === 'x'
18357             ? String.fromCharCode(parseInt(n.substring(2), 16))
18358             : String.fromCharCode(+n.substring(1));
18359         }
18360         return '';
18361       });
18362     }
18363     
18364     var replace = function (regex, opt) {
18365       regex = regex.source;
18366       opt = opt || '';
18367       return function self(name, val) {
18368         if (!name) { return new RegExp(regex, opt); }
18369         val = val.source || val;
18370         val = val.replace(/(^|[^\[])\^/g, '$1');
18371         regex = regex.replace(name, val);
18372         return self;
18373       };
18374     }
18375
18376
18377          /**
18378          * eval:var:noop
18379     */
18380     var noop = function () {}
18381     noop.exec = noop;
18382     
18383          /**
18384          * eval:var:merge
18385     */
18386     var merge = function (obj) {
18387       var i = 1
18388         , target
18389         , key;
18390     
18391       for (; i < arguments.length; i++) {
18392         target = arguments[i];
18393         for (key in target) {
18394           if (Object.prototype.hasOwnProperty.call(target, key)) {
18395             obj[key] = target[key];
18396           }
18397         }
18398       }
18399     
18400       return obj;
18401     }
18402     
18403     
18404     /**
18405      * Block-Level Grammar
18406      */
18407     
18408     
18409     
18410     
18411     var block = {
18412       newline: /^\n+/,
18413       code: /^( {4}[^\n]+\n*)+/,
18414       fences: noop,
18415       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18416       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18417       nptable: noop,
18418       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18419       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18420       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18421       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18422       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18423       table: noop,
18424       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18425       text: /^[^\n]+/
18426     };
18427     
18428     block.bullet = /(?:[*+-]|\d+\.)/;
18429     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18430     block.item = replace(block.item, 'gm')
18431       (/bull/g, block.bullet)
18432       ();
18433     
18434     block.list = replace(block.list)
18435       (/bull/g, block.bullet)
18436       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18437       ('def', '\\n+(?=' + block.def.source + ')')
18438       ();
18439     
18440     block.blockquote = replace(block.blockquote)
18441       ('def', block.def)
18442       ();
18443     
18444     block._tag = '(?!(?:'
18445       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18446       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18447       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18448     
18449     block.html = replace(block.html)
18450       ('comment', /<!--[\s\S]*?-->/)
18451       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18452       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18453       (/tag/g, block._tag)
18454       ();
18455     
18456     block.paragraph = replace(block.paragraph)
18457       ('hr', block.hr)
18458       ('heading', block.heading)
18459       ('lheading', block.lheading)
18460       ('blockquote', block.blockquote)
18461       ('tag', '<' + block._tag)
18462       ('def', block.def)
18463       ();
18464     
18465     /**
18466      * Normal Block Grammar
18467      */
18468     
18469     block.normal = merge({}, block);
18470     
18471     /**
18472      * GFM Block Grammar
18473      */
18474     
18475     block.gfm = merge({}, block.normal, {
18476       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18477       paragraph: /^/,
18478       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18479     });
18480     
18481     block.gfm.paragraph = replace(block.paragraph)
18482       ('(?!', '(?!'
18483         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18484         + block.list.source.replace('\\1', '\\3') + '|')
18485       ();
18486     
18487     /**
18488      * GFM + Tables Block Grammar
18489      */
18490     
18491     block.tables = merge({}, block.gfm, {
18492       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18493       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18494     });
18495     
18496     /**
18497      * Block Lexer
18498      */
18499     
18500     var Lexer = function (options) {
18501       this.tokens = [];
18502       this.tokens.links = {};
18503       this.options = options || marked.defaults;
18504       this.rules = block.normal;
18505     
18506       if (this.options.gfm) {
18507         if (this.options.tables) {
18508           this.rules = block.tables;
18509         } else {
18510           this.rules = block.gfm;
18511         }
18512       }
18513     }
18514     
18515     /**
18516      * Expose Block Rules
18517      */
18518     
18519     Lexer.rules = block;
18520     
18521     /**
18522      * Static Lex Method
18523      */
18524     
18525     Lexer.lex = function(src, options) {
18526       var lexer = new Lexer(options);
18527       return lexer.lex(src);
18528     };
18529     
18530     /**
18531      * Preprocessing
18532      */
18533     
18534     Lexer.prototype.lex = function(src) {
18535       src = src
18536         .replace(/\r\n|\r/g, '\n')
18537         .replace(/\t/g, '    ')
18538         .replace(/\u00a0/g, ' ')
18539         .replace(/\u2424/g, '\n');
18540     
18541       return this.token(src, true);
18542     };
18543     
18544     /**
18545      * Lexing
18546      */
18547     
18548     Lexer.prototype.token = function(src, top, bq) {
18549       var src = src.replace(/^ +$/gm, '')
18550         , next
18551         , loose
18552         , cap
18553         , bull
18554         , b
18555         , item
18556         , space
18557         , i
18558         , l;
18559     
18560       while (src) {
18561         // newline
18562         if (cap = this.rules.newline.exec(src)) {
18563           src = src.substring(cap[0].length);
18564           if (cap[0].length > 1) {
18565             this.tokens.push({
18566               type: 'space'
18567             });
18568           }
18569         }
18570     
18571         // code
18572         if (cap = this.rules.code.exec(src)) {
18573           src = src.substring(cap[0].length);
18574           cap = cap[0].replace(/^ {4}/gm, '');
18575           this.tokens.push({
18576             type: 'code',
18577             text: !this.options.pedantic
18578               ? cap.replace(/\n+$/, '')
18579               : cap
18580           });
18581           continue;
18582         }
18583     
18584         // fences (gfm)
18585         if (cap = this.rules.fences.exec(src)) {
18586           src = src.substring(cap[0].length);
18587           this.tokens.push({
18588             type: 'code',
18589             lang: cap[2],
18590             text: cap[3] || ''
18591           });
18592           continue;
18593         }
18594     
18595         // heading
18596         if (cap = this.rules.heading.exec(src)) {
18597           src = src.substring(cap[0].length);
18598           this.tokens.push({
18599             type: 'heading',
18600             depth: cap[1].length,
18601             text: cap[2]
18602           });
18603           continue;
18604         }
18605     
18606         // table no leading pipe (gfm)
18607         if (top && (cap = this.rules.nptable.exec(src))) {
18608           src = src.substring(cap[0].length);
18609     
18610           item = {
18611             type: 'table',
18612             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18613             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18614             cells: cap[3].replace(/\n$/, '').split('\n')
18615           };
18616     
18617           for (i = 0; i < item.align.length; i++) {
18618             if (/^ *-+: *$/.test(item.align[i])) {
18619               item.align[i] = 'right';
18620             } else if (/^ *:-+: *$/.test(item.align[i])) {
18621               item.align[i] = 'center';
18622             } else if (/^ *:-+ *$/.test(item.align[i])) {
18623               item.align[i] = 'left';
18624             } else {
18625               item.align[i] = null;
18626             }
18627           }
18628     
18629           for (i = 0; i < item.cells.length; i++) {
18630             item.cells[i] = item.cells[i].split(/ *\| */);
18631           }
18632     
18633           this.tokens.push(item);
18634     
18635           continue;
18636         }
18637     
18638         // lheading
18639         if (cap = this.rules.lheading.exec(src)) {
18640           src = src.substring(cap[0].length);
18641           this.tokens.push({
18642             type: 'heading',
18643             depth: cap[2] === '=' ? 1 : 2,
18644             text: cap[1]
18645           });
18646           continue;
18647         }
18648     
18649         // hr
18650         if (cap = this.rules.hr.exec(src)) {
18651           src = src.substring(cap[0].length);
18652           this.tokens.push({
18653             type: 'hr'
18654           });
18655           continue;
18656         }
18657     
18658         // blockquote
18659         if (cap = this.rules.blockquote.exec(src)) {
18660           src = src.substring(cap[0].length);
18661     
18662           this.tokens.push({
18663             type: 'blockquote_start'
18664           });
18665     
18666           cap = cap[0].replace(/^ *> ?/gm, '');
18667     
18668           // Pass `top` to keep the current
18669           // "toplevel" state. This is exactly
18670           // how markdown.pl works.
18671           this.token(cap, top, true);
18672     
18673           this.tokens.push({
18674             type: 'blockquote_end'
18675           });
18676     
18677           continue;
18678         }
18679     
18680         // list
18681         if (cap = this.rules.list.exec(src)) {
18682           src = src.substring(cap[0].length);
18683           bull = cap[2];
18684     
18685           this.tokens.push({
18686             type: 'list_start',
18687             ordered: bull.length > 1
18688           });
18689     
18690           // Get each top-level item.
18691           cap = cap[0].match(this.rules.item);
18692     
18693           next = false;
18694           l = cap.length;
18695           i = 0;
18696     
18697           for (; i < l; i++) {
18698             item = cap[i];
18699     
18700             // Remove the list item's bullet
18701             // so it is seen as the next token.
18702             space = item.length;
18703             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18704     
18705             // Outdent whatever the
18706             // list item contains. Hacky.
18707             if (~item.indexOf('\n ')) {
18708               space -= item.length;
18709               item = !this.options.pedantic
18710                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18711                 : item.replace(/^ {1,4}/gm, '');
18712             }
18713     
18714             // Determine whether the next list item belongs here.
18715             // Backpedal if it does not belong in this list.
18716             if (this.options.smartLists && i !== l - 1) {
18717               b = block.bullet.exec(cap[i + 1])[0];
18718               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18719                 src = cap.slice(i + 1).join('\n') + src;
18720                 i = l - 1;
18721               }
18722             }
18723     
18724             // Determine whether item is loose or not.
18725             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18726             // for discount behavior.
18727             loose = next || /\n\n(?!\s*$)/.test(item);
18728             if (i !== l - 1) {
18729               next = item.charAt(item.length - 1) === '\n';
18730               if (!loose) { loose = next; }
18731             }
18732     
18733             this.tokens.push({
18734               type: loose
18735                 ? 'loose_item_start'
18736                 : 'list_item_start'
18737             });
18738     
18739             // Recurse.
18740             this.token(item, false, bq);
18741     
18742             this.tokens.push({
18743               type: 'list_item_end'
18744             });
18745           }
18746     
18747           this.tokens.push({
18748             type: 'list_end'
18749           });
18750     
18751           continue;
18752         }
18753     
18754         // html
18755         if (cap = this.rules.html.exec(src)) {
18756           src = src.substring(cap[0].length);
18757           this.tokens.push({
18758             type: this.options.sanitize
18759               ? 'paragraph'
18760               : 'html',
18761             pre: !this.options.sanitizer
18762               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18763             text: cap[0]
18764           });
18765           continue;
18766         }
18767     
18768         // def
18769         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18770           src = src.substring(cap[0].length);
18771           this.tokens.links[cap[1].toLowerCase()] = {
18772             href: cap[2],
18773             title: cap[3]
18774           };
18775           continue;
18776         }
18777     
18778         // table (gfm)
18779         if (top && (cap = this.rules.table.exec(src))) {
18780           src = src.substring(cap[0].length);
18781     
18782           item = {
18783             type: 'table',
18784             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18785             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18786             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18787           };
18788     
18789           for (i = 0; i < item.align.length; i++) {
18790             if (/^ *-+: *$/.test(item.align[i])) {
18791               item.align[i] = 'right';
18792             } else if (/^ *:-+: *$/.test(item.align[i])) {
18793               item.align[i] = 'center';
18794             } else if (/^ *:-+ *$/.test(item.align[i])) {
18795               item.align[i] = 'left';
18796             } else {
18797               item.align[i] = null;
18798             }
18799           }
18800     
18801           for (i = 0; i < item.cells.length; i++) {
18802             item.cells[i] = item.cells[i]
18803               .replace(/^ *\| *| *\| *$/g, '')
18804               .split(/ *\| */);
18805           }
18806     
18807           this.tokens.push(item);
18808     
18809           continue;
18810         }
18811     
18812         // top-level paragraph
18813         if (top && (cap = this.rules.paragraph.exec(src))) {
18814           src = src.substring(cap[0].length);
18815           this.tokens.push({
18816             type: 'paragraph',
18817             text: cap[1].charAt(cap[1].length - 1) === '\n'
18818               ? cap[1].slice(0, -1)
18819               : cap[1]
18820           });
18821           continue;
18822         }
18823     
18824         // text
18825         if (cap = this.rules.text.exec(src)) {
18826           // Top-level should never reach here.
18827           src = src.substring(cap[0].length);
18828           this.tokens.push({
18829             type: 'text',
18830             text: cap[0]
18831           });
18832           continue;
18833         }
18834     
18835         if (src) {
18836           throw new
18837             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18838         }
18839       }
18840     
18841       return this.tokens;
18842     };
18843     
18844     /**
18845      * Inline-Level Grammar
18846      */
18847     
18848     var inline = {
18849       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18850       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18851       url: noop,
18852       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18853       link: /^!?\[(inside)\]\(href\)/,
18854       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18855       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18856       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18857       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18858       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18859       br: /^ {2,}\n(?!\s*$)/,
18860       del: noop,
18861       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18862     };
18863     
18864     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18865     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18866     
18867     inline.link = replace(inline.link)
18868       ('inside', inline._inside)
18869       ('href', inline._href)
18870       ();
18871     
18872     inline.reflink = replace(inline.reflink)
18873       ('inside', inline._inside)
18874       ();
18875     
18876     /**
18877      * Normal Inline Grammar
18878      */
18879     
18880     inline.normal = merge({}, inline);
18881     
18882     /**
18883      * Pedantic Inline Grammar
18884      */
18885     
18886     inline.pedantic = merge({}, inline.normal, {
18887       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18888       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18889     });
18890     
18891     /**
18892      * GFM Inline Grammar
18893      */
18894     
18895     inline.gfm = merge({}, inline.normal, {
18896       escape: replace(inline.escape)('])', '~|])')(),
18897       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18898       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18899       text: replace(inline.text)
18900         (']|', '~]|')
18901         ('|', '|https?://|')
18902         ()
18903     });
18904     
18905     /**
18906      * GFM + Line Breaks Inline Grammar
18907      */
18908     
18909     inline.breaks = merge({}, inline.gfm, {
18910       br: replace(inline.br)('{2,}', '*')(),
18911       text: replace(inline.gfm.text)('{2,}', '*')()
18912     });
18913     
18914     /**
18915      * Inline Lexer & Compiler
18916      */
18917     
18918     var InlineLexer  = function (links, options) {
18919       this.options = options || marked.defaults;
18920       this.links = links;
18921       this.rules = inline.normal;
18922       this.renderer = this.options.renderer || new Renderer;
18923       this.renderer.options = this.options;
18924     
18925       if (!this.links) {
18926         throw new
18927           Error('Tokens array requires a `links` property.');
18928       }
18929     
18930       if (this.options.gfm) {
18931         if (this.options.breaks) {
18932           this.rules = inline.breaks;
18933         } else {
18934           this.rules = inline.gfm;
18935         }
18936       } else if (this.options.pedantic) {
18937         this.rules = inline.pedantic;
18938       }
18939     }
18940     
18941     /**
18942      * Expose Inline Rules
18943      */
18944     
18945     InlineLexer.rules = inline;
18946     
18947     /**
18948      * Static Lexing/Compiling Method
18949      */
18950     
18951     InlineLexer.output = function(src, links, options) {
18952       var inline = new InlineLexer(links, options);
18953       return inline.output(src);
18954     };
18955     
18956     /**
18957      * Lexing/Compiling
18958      */
18959     
18960     InlineLexer.prototype.output = function(src) {
18961       var out = ''
18962         , link
18963         , text
18964         , href
18965         , cap;
18966     
18967       while (src) {
18968         // escape
18969         if (cap = this.rules.escape.exec(src)) {
18970           src = src.substring(cap[0].length);
18971           out += cap[1];
18972           continue;
18973         }
18974     
18975         // autolink
18976         if (cap = this.rules.autolink.exec(src)) {
18977           src = src.substring(cap[0].length);
18978           if (cap[2] === '@') {
18979             text = cap[1].charAt(6) === ':'
18980               ? this.mangle(cap[1].substring(7))
18981               : this.mangle(cap[1]);
18982             href = this.mangle('mailto:') + text;
18983           } else {
18984             text = escape(cap[1]);
18985             href = text;
18986           }
18987           out += this.renderer.link(href, null, text);
18988           continue;
18989         }
18990     
18991         // url (gfm)
18992         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18993           src = src.substring(cap[0].length);
18994           text = escape(cap[1]);
18995           href = text;
18996           out += this.renderer.link(href, null, text);
18997           continue;
18998         }
18999     
19000         // tag
19001         if (cap = this.rules.tag.exec(src)) {
19002           if (!this.inLink && /^<a /i.test(cap[0])) {
19003             this.inLink = true;
19004           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19005             this.inLink = false;
19006           }
19007           src = src.substring(cap[0].length);
19008           out += this.options.sanitize
19009             ? this.options.sanitizer
19010               ? this.options.sanitizer(cap[0])
19011               : escape(cap[0])
19012             : cap[0];
19013           continue;
19014         }
19015     
19016         // link
19017         if (cap = this.rules.link.exec(src)) {
19018           src = src.substring(cap[0].length);
19019           this.inLink = true;
19020           out += this.outputLink(cap, {
19021             href: cap[2],
19022             title: cap[3]
19023           });
19024           this.inLink = false;
19025           continue;
19026         }
19027     
19028         // reflink, nolink
19029         if ((cap = this.rules.reflink.exec(src))
19030             || (cap = this.rules.nolink.exec(src))) {
19031           src = src.substring(cap[0].length);
19032           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19033           link = this.links[link.toLowerCase()];
19034           if (!link || !link.href) {
19035             out += cap[0].charAt(0);
19036             src = cap[0].substring(1) + src;
19037             continue;
19038           }
19039           this.inLink = true;
19040           out += this.outputLink(cap, link);
19041           this.inLink = false;
19042           continue;
19043         }
19044     
19045         // strong
19046         if (cap = this.rules.strong.exec(src)) {
19047           src = src.substring(cap[0].length);
19048           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19049           continue;
19050         }
19051     
19052         // em
19053         if (cap = this.rules.em.exec(src)) {
19054           src = src.substring(cap[0].length);
19055           out += this.renderer.em(this.output(cap[2] || cap[1]));
19056           continue;
19057         }
19058     
19059         // code
19060         if (cap = this.rules.code.exec(src)) {
19061           src = src.substring(cap[0].length);
19062           out += this.renderer.codespan(escape(cap[2], true));
19063           continue;
19064         }
19065     
19066         // br
19067         if (cap = this.rules.br.exec(src)) {
19068           src = src.substring(cap[0].length);
19069           out += this.renderer.br();
19070           continue;
19071         }
19072     
19073         // del (gfm)
19074         if (cap = this.rules.del.exec(src)) {
19075           src = src.substring(cap[0].length);
19076           out += this.renderer.del(this.output(cap[1]));
19077           continue;
19078         }
19079     
19080         // text
19081         if (cap = this.rules.text.exec(src)) {
19082           src = src.substring(cap[0].length);
19083           out += this.renderer.text(escape(this.smartypants(cap[0])));
19084           continue;
19085         }
19086     
19087         if (src) {
19088           throw new
19089             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19090         }
19091       }
19092     
19093       return out;
19094     };
19095     
19096     /**
19097      * Compile Link
19098      */
19099     
19100     InlineLexer.prototype.outputLink = function(cap, link) {
19101       var href = escape(link.href)
19102         , title = link.title ? escape(link.title) : null;
19103     
19104       return cap[0].charAt(0) !== '!'
19105         ? this.renderer.link(href, title, this.output(cap[1]))
19106         : this.renderer.image(href, title, escape(cap[1]));
19107     };
19108     
19109     /**
19110      * Smartypants Transformations
19111      */
19112     
19113     InlineLexer.prototype.smartypants = function(text) {
19114       if (!this.options.smartypants)  { return text; }
19115       return text
19116         // em-dashes
19117         .replace(/---/g, '\u2014')
19118         // en-dashes
19119         .replace(/--/g, '\u2013')
19120         // opening singles
19121         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19122         // closing singles & apostrophes
19123         .replace(/'/g, '\u2019')
19124         // opening doubles
19125         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19126         // closing doubles
19127         .replace(/"/g, '\u201d')
19128         // ellipses
19129         .replace(/\.{3}/g, '\u2026');
19130     };
19131     
19132     /**
19133      * Mangle Links
19134      */
19135     
19136     InlineLexer.prototype.mangle = function(text) {
19137       if (!this.options.mangle) { return text; }
19138       var out = ''
19139         , l = text.length
19140         , i = 0
19141         , ch;
19142     
19143       for (; i < l; i++) {
19144         ch = text.charCodeAt(i);
19145         if (Math.random() > 0.5) {
19146           ch = 'x' + ch.toString(16);
19147         }
19148         out += '&#' + ch + ';';
19149       }
19150     
19151       return out;
19152     };
19153     
19154     /**
19155      * Renderer
19156      */
19157     
19158      /**
19159          * eval:var:Renderer
19160     */
19161     
19162     var Renderer   = function (options) {
19163       this.options = options || {};
19164     }
19165     
19166     Renderer.prototype.code = function(code, lang, escaped) {
19167       if (this.options.highlight) {
19168         var out = this.options.highlight(code, lang);
19169         if (out != null && out !== code) {
19170           escaped = true;
19171           code = out;
19172         }
19173       } else {
19174             // hack!!! - it's already escapeD?
19175             escaped = true;
19176       }
19177     
19178       if (!lang) {
19179         return '<pre><code>'
19180           + (escaped ? code : escape(code, true))
19181           + '\n</code></pre>';
19182       }
19183     
19184       return '<pre><code class="'
19185         + this.options.langPrefix
19186         + escape(lang, true)
19187         + '">'
19188         + (escaped ? code : escape(code, true))
19189         + '\n</code></pre>\n';
19190     };
19191     
19192     Renderer.prototype.blockquote = function(quote) {
19193       return '<blockquote>\n' + quote + '</blockquote>\n';
19194     };
19195     
19196     Renderer.prototype.html = function(html) {
19197       return html;
19198     };
19199     
19200     Renderer.prototype.heading = function(text, level, raw) {
19201       return '<h'
19202         + level
19203         + ' id="'
19204         + this.options.headerPrefix
19205         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19206         + '">'
19207         + text
19208         + '</h'
19209         + level
19210         + '>\n';
19211     };
19212     
19213     Renderer.prototype.hr = function() {
19214       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19215     };
19216     
19217     Renderer.prototype.list = function(body, ordered) {
19218       var type = ordered ? 'ol' : 'ul';
19219       return '<' + type + '>\n' + body + '</' + type + '>\n';
19220     };
19221     
19222     Renderer.prototype.listitem = function(text) {
19223       return '<li>' + text + '</li>\n';
19224     };
19225     
19226     Renderer.prototype.paragraph = function(text) {
19227       return '<p>' + text + '</p>\n';
19228     };
19229     
19230     Renderer.prototype.table = function(header, body) {
19231       return '<table class="table table-striped">\n'
19232         + '<thead>\n'
19233         + header
19234         + '</thead>\n'
19235         + '<tbody>\n'
19236         + body
19237         + '</tbody>\n'
19238         + '</table>\n';
19239     };
19240     
19241     Renderer.prototype.tablerow = function(content) {
19242       return '<tr>\n' + content + '</tr>\n';
19243     };
19244     
19245     Renderer.prototype.tablecell = function(content, flags) {
19246       var type = flags.header ? 'th' : 'td';
19247       var tag = flags.align
19248         ? '<' + type + ' style="text-align:' + flags.align + '">'
19249         : '<' + type + '>';
19250       return tag + content + '</' + type + '>\n';
19251     };
19252     
19253     // span level renderer
19254     Renderer.prototype.strong = function(text) {
19255       return '<strong>' + text + '</strong>';
19256     };
19257     
19258     Renderer.prototype.em = function(text) {
19259       return '<em>' + text + '</em>';
19260     };
19261     
19262     Renderer.prototype.codespan = function(text) {
19263       return '<code>' + text + '</code>';
19264     };
19265     
19266     Renderer.prototype.br = function() {
19267       return this.options.xhtml ? '<br/>' : '<br>';
19268     };
19269     
19270     Renderer.prototype.del = function(text) {
19271       return '<del>' + text + '</del>';
19272     };
19273     
19274     Renderer.prototype.link = function(href, title, text) {
19275       if (this.options.sanitize) {
19276         try {
19277           var prot = decodeURIComponent(unescape(href))
19278             .replace(/[^\w:]/g, '')
19279             .toLowerCase();
19280         } catch (e) {
19281           return '';
19282         }
19283         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19284           return '';
19285         }
19286       }
19287       var out = '<a href="' + href + '"';
19288       if (title) {
19289         out += ' title="' + title + '"';
19290       }
19291       out += '>' + text + '</a>';
19292       return out;
19293     };
19294     
19295     Renderer.prototype.image = function(href, title, text) {
19296       var out = '<img src="' + href + '" alt="' + text + '"';
19297       if (title) {
19298         out += ' title="' + title + '"';
19299       }
19300       out += this.options.xhtml ? '/>' : '>';
19301       return out;
19302     };
19303     
19304     Renderer.prototype.text = function(text) {
19305       return text;
19306     };
19307     
19308     /**
19309      * Parsing & Compiling
19310      */
19311          /**
19312          * eval:var:Parser
19313     */
19314     
19315     var Parser= function (options) {
19316       this.tokens = [];
19317       this.token = null;
19318       this.options = options || marked.defaults;
19319       this.options.renderer = this.options.renderer || new Renderer;
19320       this.renderer = this.options.renderer;
19321       this.renderer.options = this.options;
19322     }
19323     
19324     /**
19325      * Static Parse Method
19326      */
19327     
19328     Parser.parse = function(src, options, renderer) {
19329       var parser = new Parser(options, renderer);
19330       return parser.parse(src);
19331     };
19332     
19333     /**
19334      * Parse Loop
19335      */
19336     
19337     Parser.prototype.parse = function(src) {
19338       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19339       this.tokens = src.reverse();
19340     
19341       var out = '';
19342       while (this.next()) {
19343         out += this.tok();
19344       }
19345     
19346       return out;
19347     };
19348     
19349     /**
19350      * Next Token
19351      */
19352     
19353     Parser.prototype.next = function() {
19354       return this.token = this.tokens.pop();
19355     };
19356     
19357     /**
19358      * Preview Next Token
19359      */
19360     
19361     Parser.prototype.peek = function() {
19362       return this.tokens[this.tokens.length - 1] || 0;
19363     };
19364     
19365     /**
19366      * Parse Text Tokens
19367      */
19368     
19369     Parser.prototype.parseText = function() {
19370       var body = this.token.text;
19371     
19372       while (this.peek().type === 'text') {
19373         body += '\n' + this.next().text;
19374       }
19375     
19376       return this.inline.output(body);
19377     };
19378     
19379     /**
19380      * Parse Current Token
19381      */
19382     
19383     Parser.prototype.tok = function() {
19384       switch (this.token.type) {
19385         case 'space': {
19386           return '';
19387         }
19388         case 'hr': {
19389           return this.renderer.hr();
19390         }
19391         case 'heading': {
19392           return this.renderer.heading(
19393             this.inline.output(this.token.text),
19394             this.token.depth,
19395             this.token.text);
19396         }
19397         case 'code': {
19398           return this.renderer.code(this.token.text,
19399             this.token.lang,
19400             this.token.escaped);
19401         }
19402         case 'table': {
19403           var header = ''
19404             , body = ''
19405             , i
19406             , row
19407             , cell
19408             , flags
19409             , j;
19410     
19411           // header
19412           cell = '';
19413           for (i = 0; i < this.token.header.length; i++) {
19414             flags = { header: true, align: this.token.align[i] };
19415             cell += this.renderer.tablecell(
19416               this.inline.output(this.token.header[i]),
19417               { header: true, align: this.token.align[i] }
19418             );
19419           }
19420           header += this.renderer.tablerow(cell);
19421     
19422           for (i = 0; i < this.token.cells.length; i++) {
19423             row = this.token.cells[i];
19424     
19425             cell = '';
19426             for (j = 0; j < row.length; j++) {
19427               cell += this.renderer.tablecell(
19428                 this.inline.output(row[j]),
19429                 { header: false, align: this.token.align[j] }
19430               );
19431             }
19432     
19433             body += this.renderer.tablerow(cell);
19434           }
19435           return this.renderer.table(header, body);
19436         }
19437         case 'blockquote_start': {
19438           var body = '';
19439     
19440           while (this.next().type !== 'blockquote_end') {
19441             body += this.tok();
19442           }
19443     
19444           return this.renderer.blockquote(body);
19445         }
19446         case 'list_start': {
19447           var body = ''
19448             , ordered = this.token.ordered;
19449     
19450           while (this.next().type !== 'list_end') {
19451             body += this.tok();
19452           }
19453     
19454           return this.renderer.list(body, ordered);
19455         }
19456         case 'list_item_start': {
19457           var body = '';
19458     
19459           while (this.next().type !== 'list_item_end') {
19460             body += this.token.type === 'text'
19461               ? this.parseText()
19462               : this.tok();
19463           }
19464     
19465           return this.renderer.listitem(body);
19466         }
19467         case 'loose_item_start': {
19468           var body = '';
19469     
19470           while (this.next().type !== 'list_item_end') {
19471             body += this.tok();
19472           }
19473     
19474           return this.renderer.listitem(body);
19475         }
19476         case 'html': {
19477           var html = !this.token.pre && !this.options.pedantic
19478             ? this.inline.output(this.token.text)
19479             : this.token.text;
19480           return this.renderer.html(html);
19481         }
19482         case 'paragraph': {
19483           return this.renderer.paragraph(this.inline.output(this.token.text));
19484         }
19485         case 'text': {
19486           return this.renderer.paragraph(this.parseText());
19487         }
19488       }
19489     };
19490   
19491     
19492     /**
19493      * Marked
19494      */
19495          /**
19496          * eval:var:marked
19497     */
19498     var marked = function (src, opt, callback) {
19499       if (callback || typeof opt === 'function') {
19500         if (!callback) {
19501           callback = opt;
19502           opt = null;
19503         }
19504     
19505         opt = merge({}, marked.defaults, opt || {});
19506     
19507         var highlight = opt.highlight
19508           , tokens
19509           , pending
19510           , i = 0;
19511     
19512         try {
19513           tokens = Lexer.lex(src, opt)
19514         } catch (e) {
19515           return callback(e);
19516         }
19517     
19518         pending = tokens.length;
19519          /**
19520          * eval:var:done
19521     */
19522         var done = function(err) {
19523           if (err) {
19524             opt.highlight = highlight;
19525             return callback(err);
19526           }
19527     
19528           var out;
19529     
19530           try {
19531             out = Parser.parse(tokens, opt);
19532           } catch (e) {
19533             err = e;
19534           }
19535     
19536           opt.highlight = highlight;
19537     
19538           return err
19539             ? callback(err)
19540             : callback(null, out);
19541         };
19542     
19543         if (!highlight || highlight.length < 3) {
19544           return done();
19545         }
19546     
19547         delete opt.highlight;
19548     
19549         if (!pending) { return done(); }
19550     
19551         for (; i < tokens.length; i++) {
19552           (function(token) {
19553             if (token.type !== 'code') {
19554               return --pending || done();
19555             }
19556             return highlight(token.text, token.lang, function(err, code) {
19557               if (err) { return done(err); }
19558               if (code == null || code === token.text) {
19559                 return --pending || done();
19560               }
19561               token.text = code;
19562               token.escaped = true;
19563               --pending || done();
19564             });
19565           })(tokens[i]);
19566         }
19567     
19568         return;
19569       }
19570       try {
19571         if (opt) { opt = merge({}, marked.defaults, opt); }
19572         return Parser.parse(Lexer.lex(src, opt), opt);
19573       } catch (e) {
19574         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19575         if ((opt || marked.defaults).silent) {
19576           return '<p>An error occured:</p><pre>'
19577             + escape(e.message + '', true)
19578             + '</pre>';
19579         }
19580         throw e;
19581       }
19582     }
19583     
19584     /**
19585      * Options
19586      */
19587     
19588     marked.options =
19589     marked.setOptions = function(opt) {
19590       merge(marked.defaults, opt);
19591       return marked;
19592     };
19593     
19594     marked.defaults = {
19595       gfm: true,
19596       tables: true,
19597       breaks: false,
19598       pedantic: false,
19599       sanitize: false,
19600       sanitizer: null,
19601       mangle: true,
19602       smartLists: false,
19603       silent: false,
19604       highlight: null,
19605       langPrefix: 'lang-',
19606       smartypants: false,
19607       headerPrefix: '',
19608       renderer: new Renderer,
19609       xhtml: false
19610     };
19611     
19612     /**
19613      * Expose
19614      */
19615     
19616     marked.Parser = Parser;
19617     marked.parser = Parser.parse;
19618     
19619     marked.Renderer = Renderer;
19620     
19621     marked.Lexer = Lexer;
19622     marked.lexer = Lexer.lex;
19623     
19624     marked.InlineLexer = InlineLexer;
19625     marked.inlineLexer = InlineLexer.output;
19626     
19627     marked.parse = marked;
19628     
19629     Roo.Markdown.marked = marked;
19630
19631 })();/*
19632  * Based on:
19633  * Ext JS Library 1.1.1
19634  * Copyright(c) 2006-2007, Ext JS, LLC.
19635  *
19636  * Originally Released Under LGPL - original licence link has changed is not relivant.
19637  *
19638  * Fork - LGPL
19639  * <script type="text/javascript">
19640  */
19641
19642
19643
19644 /*
19645  * These classes are derivatives of the similarly named classes in the YUI Library.
19646  * The original license:
19647  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19648  * Code licensed under the BSD License:
19649  * http://developer.yahoo.net/yui/license.txt
19650  */
19651
19652 (function() {
19653
19654 var Event=Roo.EventManager;
19655 var Dom=Roo.lib.Dom;
19656
19657 /**
19658  * @class Roo.dd.DragDrop
19659  * @extends Roo.util.Observable
19660  * Defines the interface and base operation of items that that can be
19661  * dragged or can be drop targets.  It was designed to be extended, overriding
19662  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19663  * Up to three html elements can be associated with a DragDrop instance:
19664  * <ul>
19665  * <li>linked element: the element that is passed into the constructor.
19666  * This is the element which defines the boundaries for interaction with
19667  * other DragDrop objects.</li>
19668  * <li>handle element(s): The drag operation only occurs if the element that
19669  * was clicked matches a handle element.  By default this is the linked
19670  * element, but there are times that you will want only a portion of the
19671  * linked element to initiate the drag operation, and the setHandleElId()
19672  * method provides a way to define this.</li>
19673  * <li>drag element: this represents the element that would be moved along
19674  * with the cursor during a drag operation.  By default, this is the linked
19675  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19676  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19677  * </li>
19678  * </ul>
19679  * This class should not be instantiated until the onload event to ensure that
19680  * the associated elements are available.
19681  * The following would define a DragDrop obj that would interact with any
19682  * other DragDrop obj in the "group1" group:
19683  * <pre>
19684  *  dd = new Roo.dd.DragDrop("div1", "group1");
19685  * </pre>
19686  * Since none of the event handlers have been implemented, nothing would
19687  * actually happen if you were to run the code above.  Normally you would
19688  * override this class or one of the default implementations, but you can
19689  * also override the methods you want on an instance of the class...
19690  * <pre>
19691  *  dd.onDragDrop = function(e, id) {
19692  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19693  *  }
19694  * </pre>
19695  * @constructor
19696  * @param {String} id of the element that is linked to this instance
19697  * @param {String} sGroup the group of related DragDrop objects
19698  * @param {object} config an object containing configurable attributes
19699  *                Valid properties for DragDrop:
19700  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19701  */
19702 Roo.dd.DragDrop = function(id, sGroup, config) {
19703     if (id) {
19704         this.init(id, sGroup, config);
19705     }
19706     
19707 };
19708
19709 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19710
19711     /**
19712      * The id of the element associated with this object.  This is what we
19713      * refer to as the "linked element" because the size and position of
19714      * this element is used to determine when the drag and drop objects have
19715      * interacted.
19716      * @property id
19717      * @type String
19718      */
19719     id: null,
19720
19721     /**
19722      * Configuration attributes passed into the constructor
19723      * @property config
19724      * @type object
19725      */
19726     config: null,
19727
19728     /**
19729      * The id of the element that will be dragged.  By default this is same
19730      * as the linked element , but could be changed to another element. Ex:
19731      * Roo.dd.DDProxy
19732      * @property dragElId
19733      * @type String
19734      * @private
19735      */
19736     dragElId: null,
19737
19738     /**
19739      * the id of the element that initiates the drag operation.  By default
19740      * this is the linked element, but could be changed to be a child of this
19741      * element.  This lets us do things like only starting the drag when the
19742      * header element within the linked html element is clicked.
19743      * @property handleElId
19744      * @type String
19745      * @private
19746      */
19747     handleElId: null,
19748
19749     /**
19750      * An associative array of HTML tags that will be ignored if clicked.
19751      * @property invalidHandleTypes
19752      * @type {string: string}
19753      */
19754     invalidHandleTypes: null,
19755
19756     /**
19757      * An associative array of ids for elements that will be ignored if clicked
19758      * @property invalidHandleIds
19759      * @type {string: string}
19760      */
19761     invalidHandleIds: null,
19762
19763     /**
19764      * An indexted array of css class names for elements that will be ignored
19765      * if clicked.
19766      * @property invalidHandleClasses
19767      * @type string[]
19768      */
19769     invalidHandleClasses: null,
19770
19771     /**
19772      * The linked element's absolute X position at the time the drag was
19773      * started
19774      * @property startPageX
19775      * @type int
19776      * @private
19777      */
19778     startPageX: 0,
19779
19780     /**
19781      * The linked element's absolute X position at the time the drag was
19782      * started
19783      * @property startPageY
19784      * @type int
19785      * @private
19786      */
19787     startPageY: 0,
19788
19789     /**
19790      * The group defines a logical collection of DragDrop objects that are
19791      * related.  Instances only get events when interacting with other
19792      * DragDrop object in the same group.  This lets us define multiple
19793      * groups using a single DragDrop subclass if we want.
19794      * @property groups
19795      * @type {string: string}
19796      */
19797     groups: null,
19798
19799     /**
19800      * Individual drag/drop instances can be locked.  This will prevent
19801      * onmousedown start drag.
19802      * @property locked
19803      * @type boolean
19804      * @private
19805      */
19806     locked: false,
19807
19808     /**
19809      * Lock this instance
19810      * @method lock
19811      */
19812     lock: function() { this.locked = true; },
19813
19814     /**
19815      * Unlock this instace
19816      * @method unlock
19817      */
19818     unlock: function() { this.locked = false; },
19819
19820     /**
19821      * By default, all insances can be a drop target.  This can be disabled by
19822      * setting isTarget to false.
19823      * @method isTarget
19824      * @type boolean
19825      */
19826     isTarget: true,
19827
19828     /**
19829      * The padding configured for this drag and drop object for calculating
19830      * the drop zone intersection with this object.
19831      * @method padding
19832      * @type int[]
19833      */
19834     padding: null,
19835
19836     /**
19837      * Cached reference to the linked element
19838      * @property _domRef
19839      * @private
19840      */
19841     _domRef: null,
19842
19843     /**
19844      * Internal typeof flag
19845      * @property __ygDragDrop
19846      * @private
19847      */
19848     __ygDragDrop: true,
19849
19850     /**
19851      * Set to true when horizontal contraints are applied
19852      * @property constrainX
19853      * @type boolean
19854      * @private
19855      */
19856     constrainX: false,
19857
19858     /**
19859      * Set to true when vertical contraints are applied
19860      * @property constrainY
19861      * @type boolean
19862      * @private
19863      */
19864     constrainY: false,
19865
19866     /**
19867      * The left constraint
19868      * @property minX
19869      * @type int
19870      * @private
19871      */
19872     minX: 0,
19873
19874     /**
19875      * The right constraint
19876      * @property maxX
19877      * @type int
19878      * @private
19879      */
19880     maxX: 0,
19881
19882     /**
19883      * The up constraint
19884      * @property minY
19885      * @type int
19886      * @type int
19887      * @private
19888      */
19889     minY: 0,
19890
19891     /**
19892      * The down constraint
19893      * @property maxY
19894      * @type int
19895      * @private
19896      */
19897     maxY: 0,
19898
19899     /**
19900      * Maintain offsets when we resetconstraints.  Set to true when you want
19901      * the position of the element relative to its parent to stay the same
19902      * when the page changes
19903      *
19904      * @property maintainOffset
19905      * @type boolean
19906      */
19907     maintainOffset: false,
19908
19909     /**
19910      * Array of pixel locations the element will snap to if we specified a
19911      * horizontal graduation/interval.  This array is generated automatically
19912      * when you define a tick interval.
19913      * @property xTicks
19914      * @type int[]
19915      */
19916     xTicks: null,
19917
19918     /**
19919      * Array of pixel locations the element will snap to if we specified a
19920      * vertical graduation/interval.  This array is generated automatically
19921      * when you define a tick interval.
19922      * @property yTicks
19923      * @type int[]
19924      */
19925     yTicks: null,
19926
19927     /**
19928      * By default the drag and drop instance will only respond to the primary
19929      * button click (left button for a right-handed mouse).  Set to true to
19930      * allow drag and drop to start with any mouse click that is propogated
19931      * by the browser
19932      * @property primaryButtonOnly
19933      * @type boolean
19934      */
19935     primaryButtonOnly: true,
19936
19937     /**
19938      * The availabe property is false until the linked dom element is accessible.
19939      * @property available
19940      * @type boolean
19941      */
19942     available: false,
19943
19944     /**
19945      * By default, drags can only be initiated if the mousedown occurs in the
19946      * region the linked element is.  This is done in part to work around a
19947      * bug in some browsers that mis-report the mousedown if the previous
19948      * mouseup happened outside of the window.  This property is set to true
19949      * if outer handles are defined.
19950      *
19951      * @property hasOuterHandles
19952      * @type boolean
19953      * @default false
19954      */
19955     hasOuterHandles: false,
19956
19957     /**
19958      * Code that executes immediately before the startDrag event
19959      * @method b4StartDrag
19960      * @private
19961      */
19962     b4StartDrag: function(x, y) { },
19963
19964     /**
19965      * Abstract method called after a drag/drop object is clicked
19966      * and the drag or mousedown time thresholds have beeen met.
19967      * @method startDrag
19968      * @param {int} X click location
19969      * @param {int} Y click location
19970      */
19971     startDrag: function(x, y) { /* override this */ },
19972
19973     /**
19974      * Code that executes immediately before the onDrag event
19975      * @method b4Drag
19976      * @private
19977      */
19978     b4Drag: function(e) { },
19979
19980     /**
19981      * Abstract method called during the onMouseMove event while dragging an
19982      * object.
19983      * @method onDrag
19984      * @param {Event} e the mousemove event
19985      */
19986     onDrag: function(e) { /* override this */ },
19987
19988     /**
19989      * Abstract method called when this element fist begins hovering over
19990      * another DragDrop obj
19991      * @method onDragEnter
19992      * @param {Event} e the mousemove event
19993      * @param {String|DragDrop[]} id In POINT mode, the element
19994      * id this is hovering over.  In INTERSECT mode, an array of one or more
19995      * dragdrop items being hovered over.
19996      */
19997     onDragEnter: function(e, id) { /* override this */ },
19998
19999     /**
20000      * Code that executes immediately before the onDragOver event
20001      * @method b4DragOver
20002      * @private
20003      */
20004     b4DragOver: function(e) { },
20005
20006     /**
20007      * Abstract method called when this element is hovering over another
20008      * DragDrop obj
20009      * @method onDragOver
20010      * @param {Event} e the mousemove event
20011      * @param {String|DragDrop[]} id In POINT mode, the element
20012      * id this is hovering over.  In INTERSECT mode, an array of dd items
20013      * being hovered over.
20014      */
20015     onDragOver: function(e, id) { /* override this */ },
20016
20017     /**
20018      * Code that executes immediately before the onDragOut event
20019      * @method b4DragOut
20020      * @private
20021      */
20022     b4DragOut: function(e) { },
20023
20024     /**
20025      * Abstract method called when we are no longer hovering over an element
20026      * @method onDragOut
20027      * @param {Event} e the mousemove event
20028      * @param {String|DragDrop[]} id In POINT mode, the element
20029      * id this was hovering over.  In INTERSECT mode, an array of dd items
20030      * that the mouse is no longer over.
20031      */
20032     onDragOut: function(e, id) { /* override this */ },
20033
20034     /**
20035      * Code that executes immediately before the onDragDrop event
20036      * @method b4DragDrop
20037      * @private
20038      */
20039     b4DragDrop: function(e) { },
20040
20041     /**
20042      * Abstract method called when this item is dropped on another DragDrop
20043      * obj
20044      * @method onDragDrop
20045      * @param {Event} e the mouseup event
20046      * @param {String|DragDrop[]} id In POINT mode, the element
20047      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20048      * was dropped on.
20049      */
20050     onDragDrop: function(e, id) { /* override this */ },
20051
20052     /**
20053      * Abstract method called when this item is dropped on an area with no
20054      * drop target
20055      * @method onInvalidDrop
20056      * @param {Event} e the mouseup event
20057      */
20058     onInvalidDrop: function(e) { /* override this */ },
20059
20060     /**
20061      * Code that executes immediately before the endDrag event
20062      * @method b4EndDrag
20063      * @private
20064      */
20065     b4EndDrag: function(e) { },
20066
20067     /**
20068      * Fired when we are done dragging the object
20069      * @method endDrag
20070      * @param {Event} e the mouseup event
20071      */
20072     endDrag: function(e) { /* override this */ },
20073
20074     /**
20075      * Code executed immediately before the onMouseDown event
20076      * @method b4MouseDown
20077      * @param {Event} e the mousedown event
20078      * @private
20079      */
20080     b4MouseDown: function(e) {  },
20081
20082     /**
20083      * Event handler that fires when a drag/drop obj gets a mousedown
20084      * @method onMouseDown
20085      * @param {Event} e the mousedown event
20086      */
20087     onMouseDown: function(e) { /* override this */ },
20088
20089     /**
20090      * Event handler that fires when a drag/drop obj gets a mouseup
20091      * @method onMouseUp
20092      * @param {Event} e the mouseup event
20093      */
20094     onMouseUp: function(e) { /* override this */ },
20095
20096     /**
20097      * Override the onAvailable method to do what is needed after the initial
20098      * position was determined.
20099      * @method onAvailable
20100      */
20101     onAvailable: function () {
20102     },
20103
20104     /*
20105      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20106      * @type Object
20107      */
20108     defaultPadding : {left:0, right:0, top:0, bottom:0},
20109
20110     /*
20111      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20112  *
20113  * Usage:
20114  <pre><code>
20115  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20116                 { dragElId: "existingProxyDiv" });
20117  dd.startDrag = function(){
20118      this.constrainTo("parent-id");
20119  };
20120  </code></pre>
20121  * Or you can initalize it using the {@link Roo.Element} object:
20122  <pre><code>
20123  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20124      startDrag : function(){
20125          this.constrainTo("parent-id");
20126      }
20127  });
20128  </code></pre>
20129      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20130      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20131      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20132      * an object containing the sides to pad. For example: {right:10, bottom:10}
20133      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20134      */
20135     constrainTo : function(constrainTo, pad, inContent){
20136         if(typeof pad == "number"){
20137             pad = {left: pad, right:pad, top:pad, bottom:pad};
20138         }
20139         pad = pad || this.defaultPadding;
20140         var b = Roo.get(this.getEl()).getBox();
20141         var ce = Roo.get(constrainTo);
20142         var s = ce.getScroll();
20143         var c, cd = ce.dom;
20144         if(cd == document.body){
20145             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20146         }else{
20147             xy = ce.getXY();
20148             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20149         }
20150
20151
20152         var topSpace = b.y - c.y;
20153         var leftSpace = b.x - c.x;
20154
20155         this.resetConstraints();
20156         this.setXConstraint(leftSpace - (pad.left||0), // left
20157                 c.width - leftSpace - b.width - (pad.right||0) //right
20158         );
20159         this.setYConstraint(topSpace - (pad.top||0), //top
20160                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20161         );
20162     },
20163
20164     /**
20165      * Returns a reference to the linked element
20166      * @method getEl
20167      * @return {HTMLElement} the html element
20168      */
20169     getEl: function() {
20170         if (!this._domRef) {
20171             this._domRef = Roo.getDom(this.id);
20172         }
20173
20174         return this._domRef;
20175     },
20176
20177     /**
20178      * Returns a reference to the actual element to drag.  By default this is
20179      * the same as the html element, but it can be assigned to another
20180      * element. An example of this can be found in Roo.dd.DDProxy
20181      * @method getDragEl
20182      * @return {HTMLElement} the html element
20183      */
20184     getDragEl: function() {
20185         return Roo.getDom(this.dragElId);
20186     },
20187
20188     /**
20189      * Sets up the DragDrop object.  Must be called in the constructor of any
20190      * Roo.dd.DragDrop subclass
20191      * @method init
20192      * @param id the id of the linked element
20193      * @param {String} sGroup the group of related items
20194      * @param {object} config configuration attributes
20195      */
20196     init: function(id, sGroup, config) {
20197         this.initTarget(id, sGroup, config);
20198         if (!Roo.isTouch) {
20199             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20200         }
20201         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20202         // Event.on(this.id, "selectstart", Event.preventDefault);
20203     },
20204
20205     /**
20206      * Initializes Targeting functionality only... the object does not
20207      * get a mousedown handler.
20208      * @method initTarget
20209      * @param id the id of the linked element
20210      * @param {String} sGroup the group of related items
20211      * @param {object} config configuration attributes
20212      */
20213     initTarget: function(id, sGroup, config) {
20214
20215         // configuration attributes
20216         this.config = config || {};
20217
20218         // create a local reference to the drag and drop manager
20219         this.DDM = Roo.dd.DDM;
20220         // initialize the groups array
20221         this.groups = {};
20222
20223         // assume that we have an element reference instead of an id if the
20224         // parameter is not a string
20225         if (typeof id !== "string") {
20226             id = Roo.id(id);
20227         }
20228
20229         // set the id
20230         this.id = id;
20231
20232         // add to an interaction group
20233         this.addToGroup((sGroup) ? sGroup : "default");
20234
20235         // We don't want to register this as the handle with the manager
20236         // so we just set the id rather than calling the setter.
20237         this.handleElId = id;
20238
20239         // the linked element is the element that gets dragged by default
20240         this.setDragElId(id);
20241
20242         // by default, clicked anchors will not start drag operations.
20243         this.invalidHandleTypes = { A: "A" };
20244         this.invalidHandleIds = {};
20245         this.invalidHandleClasses = [];
20246
20247         this.applyConfig();
20248
20249         this.handleOnAvailable();
20250     },
20251
20252     /**
20253      * Applies the configuration parameters that were passed into the constructor.
20254      * This is supposed to happen at each level through the inheritance chain.  So
20255      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20256      * DragDrop in order to get all of the parameters that are available in
20257      * each object.
20258      * @method applyConfig
20259      */
20260     applyConfig: function() {
20261
20262         // configurable properties:
20263         //    padding, isTarget, maintainOffset, primaryButtonOnly
20264         this.padding           = this.config.padding || [0, 0, 0, 0];
20265         this.isTarget          = (this.config.isTarget !== false);
20266         this.maintainOffset    = (this.config.maintainOffset);
20267         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20268
20269     },
20270
20271     /**
20272      * Executed when the linked element is available
20273      * @method handleOnAvailable
20274      * @private
20275      */
20276     handleOnAvailable: function() {
20277         this.available = true;
20278         this.resetConstraints();
20279         this.onAvailable();
20280     },
20281
20282      /**
20283      * Configures the padding for the target zone in px.  Effectively expands
20284      * (or reduces) the virtual object size for targeting calculations.
20285      * Supports css-style shorthand; if only one parameter is passed, all sides
20286      * will have that padding, and if only two are passed, the top and bottom
20287      * will have the first param, the left and right the second.
20288      * @method setPadding
20289      * @param {int} iTop    Top pad
20290      * @param {int} iRight  Right pad
20291      * @param {int} iBot    Bot pad
20292      * @param {int} iLeft   Left pad
20293      */
20294     setPadding: function(iTop, iRight, iBot, iLeft) {
20295         // this.padding = [iLeft, iRight, iTop, iBot];
20296         if (!iRight && 0 !== iRight) {
20297             this.padding = [iTop, iTop, iTop, iTop];
20298         } else if (!iBot && 0 !== iBot) {
20299             this.padding = [iTop, iRight, iTop, iRight];
20300         } else {
20301             this.padding = [iTop, iRight, iBot, iLeft];
20302         }
20303     },
20304
20305     /**
20306      * Stores the initial placement of the linked element.
20307      * @method setInitialPosition
20308      * @param {int} diffX   the X offset, default 0
20309      * @param {int} diffY   the Y offset, default 0
20310      */
20311     setInitPosition: function(diffX, diffY) {
20312         var el = this.getEl();
20313
20314         if (!this.DDM.verifyEl(el)) {
20315             return;
20316         }
20317
20318         var dx = diffX || 0;
20319         var dy = diffY || 0;
20320
20321         var p = Dom.getXY( el );
20322
20323         this.initPageX = p[0] - dx;
20324         this.initPageY = p[1] - dy;
20325
20326         this.lastPageX = p[0];
20327         this.lastPageY = p[1];
20328
20329
20330         this.setStartPosition(p);
20331     },
20332
20333     /**
20334      * Sets the start position of the element.  This is set when the obj
20335      * is initialized, the reset when a drag is started.
20336      * @method setStartPosition
20337      * @param pos current position (from previous lookup)
20338      * @private
20339      */
20340     setStartPosition: function(pos) {
20341         var p = pos || Dom.getXY( this.getEl() );
20342         this.deltaSetXY = null;
20343
20344         this.startPageX = p[0];
20345         this.startPageY = p[1];
20346     },
20347
20348     /**
20349      * Add this instance to a group of related drag/drop objects.  All
20350      * instances belong to at least one group, and can belong to as many
20351      * groups as needed.
20352      * @method addToGroup
20353      * @param sGroup {string} the name of the group
20354      */
20355     addToGroup: function(sGroup) {
20356         this.groups[sGroup] = true;
20357         this.DDM.regDragDrop(this, sGroup);
20358     },
20359
20360     /**
20361      * Remove's this instance from the supplied interaction group
20362      * @method removeFromGroup
20363      * @param {string}  sGroup  The group to drop
20364      */
20365     removeFromGroup: function(sGroup) {
20366         if (this.groups[sGroup]) {
20367             delete this.groups[sGroup];
20368         }
20369
20370         this.DDM.removeDDFromGroup(this, sGroup);
20371     },
20372
20373     /**
20374      * Allows you to specify that an element other than the linked element
20375      * will be moved with the cursor during a drag
20376      * @method setDragElId
20377      * @param id {string} the id of the element that will be used to initiate the drag
20378      */
20379     setDragElId: function(id) {
20380         this.dragElId = id;
20381     },
20382
20383     /**
20384      * Allows you to specify a child of the linked element that should be
20385      * used to initiate the drag operation.  An example of this would be if
20386      * you have a content div with text and links.  Clicking anywhere in the
20387      * content area would normally start the drag operation.  Use this method
20388      * to specify that an element inside of the content div is the element
20389      * that starts the drag operation.
20390      * @method setHandleElId
20391      * @param id {string} the id of the element that will be used to
20392      * initiate the drag.
20393      */
20394     setHandleElId: function(id) {
20395         if (typeof id !== "string") {
20396             id = Roo.id(id);
20397         }
20398         this.handleElId = id;
20399         this.DDM.regHandle(this.id, id);
20400     },
20401
20402     /**
20403      * Allows you to set an element outside of the linked element as a drag
20404      * handle
20405      * @method setOuterHandleElId
20406      * @param id the id of the element that will be used to initiate the drag
20407      */
20408     setOuterHandleElId: function(id) {
20409         if (typeof id !== "string") {
20410             id = Roo.id(id);
20411         }
20412         Event.on(id, "mousedown",
20413                 this.handleMouseDown, this);
20414         this.setHandleElId(id);
20415
20416         this.hasOuterHandles = true;
20417     },
20418
20419     /**
20420      * Remove all drag and drop hooks for this element
20421      * @method unreg
20422      */
20423     unreg: function() {
20424         Event.un(this.id, "mousedown",
20425                 this.handleMouseDown);
20426         Event.un(this.id, "touchstart",
20427                 this.handleMouseDown);
20428         this._domRef = null;
20429         this.DDM._remove(this);
20430     },
20431
20432     destroy : function(){
20433         this.unreg();
20434     },
20435
20436     /**
20437      * Returns true if this instance is locked, or the drag drop mgr is locked
20438      * (meaning that all drag/drop is disabled on the page.)
20439      * @method isLocked
20440      * @return {boolean} true if this obj or all drag/drop is locked, else
20441      * false
20442      */
20443     isLocked: function() {
20444         return (this.DDM.isLocked() || this.locked);
20445     },
20446
20447     /**
20448      * Fired when this object is clicked
20449      * @method handleMouseDown
20450      * @param {Event} e
20451      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20452      * @private
20453      */
20454     handleMouseDown: function(e, oDD){
20455      
20456         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20457             //Roo.log('not touch/ button !=0');
20458             return;
20459         }
20460         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20461             return; // double touch..
20462         }
20463         
20464
20465         if (this.isLocked()) {
20466             //Roo.log('locked');
20467             return;
20468         }
20469
20470         this.DDM.refreshCache(this.groups);
20471 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20472         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20473         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20474             //Roo.log('no outer handes or not over target');
20475                 // do nothing.
20476         } else {
20477 //            Roo.log('check validator');
20478             if (this.clickValidator(e)) {
20479 //                Roo.log('validate success');
20480                 // set the initial element position
20481                 this.setStartPosition();
20482
20483
20484                 this.b4MouseDown(e);
20485                 this.onMouseDown(e);
20486
20487                 this.DDM.handleMouseDown(e, this);
20488
20489                 this.DDM.stopEvent(e);
20490             } else {
20491
20492
20493             }
20494         }
20495     },
20496
20497     clickValidator: function(e) {
20498         var target = e.getTarget();
20499         return ( this.isValidHandleChild(target) &&
20500                     (this.id == this.handleElId ||
20501                         this.DDM.handleWasClicked(target, this.id)) );
20502     },
20503
20504     /**
20505      * Allows you to specify a tag name that should not start a drag operation
20506      * when clicked.  This is designed to facilitate embedding links within a
20507      * drag handle that do something other than start the drag.
20508      * @method addInvalidHandleType
20509      * @param {string} tagName the type of element to exclude
20510      */
20511     addInvalidHandleType: function(tagName) {
20512         var type = tagName.toUpperCase();
20513         this.invalidHandleTypes[type] = type;
20514     },
20515
20516     /**
20517      * Lets you to specify an element id for a child of a drag handle
20518      * that should not initiate a drag
20519      * @method addInvalidHandleId
20520      * @param {string} id the element id of the element you wish to ignore
20521      */
20522     addInvalidHandleId: function(id) {
20523         if (typeof id !== "string") {
20524             id = Roo.id(id);
20525         }
20526         this.invalidHandleIds[id] = id;
20527     },
20528
20529     /**
20530      * Lets you specify a css class of elements that will not initiate a drag
20531      * @method addInvalidHandleClass
20532      * @param {string} cssClass the class of the elements you wish to ignore
20533      */
20534     addInvalidHandleClass: function(cssClass) {
20535         this.invalidHandleClasses.push(cssClass);
20536     },
20537
20538     /**
20539      * Unsets an excluded tag name set by addInvalidHandleType
20540      * @method removeInvalidHandleType
20541      * @param {string} tagName the type of element to unexclude
20542      */
20543     removeInvalidHandleType: function(tagName) {
20544         var type = tagName.toUpperCase();
20545         // this.invalidHandleTypes[type] = null;
20546         delete this.invalidHandleTypes[type];
20547     },
20548
20549     /**
20550      * Unsets an invalid handle id
20551      * @method removeInvalidHandleId
20552      * @param {string} id the id of the element to re-enable
20553      */
20554     removeInvalidHandleId: function(id) {
20555         if (typeof id !== "string") {
20556             id = Roo.id(id);
20557         }
20558         delete this.invalidHandleIds[id];
20559     },
20560
20561     /**
20562      * Unsets an invalid css class
20563      * @method removeInvalidHandleClass
20564      * @param {string} cssClass the class of the element(s) you wish to
20565      * re-enable
20566      */
20567     removeInvalidHandleClass: function(cssClass) {
20568         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20569             if (this.invalidHandleClasses[i] == cssClass) {
20570                 delete this.invalidHandleClasses[i];
20571             }
20572         }
20573     },
20574
20575     /**
20576      * Checks the tag exclusion list to see if this click should be ignored
20577      * @method isValidHandleChild
20578      * @param {HTMLElement} node the HTMLElement to evaluate
20579      * @return {boolean} true if this is a valid tag type, false if not
20580      */
20581     isValidHandleChild: function(node) {
20582
20583         var valid = true;
20584         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20585         var nodeName;
20586         try {
20587             nodeName = node.nodeName.toUpperCase();
20588         } catch(e) {
20589             nodeName = node.nodeName;
20590         }
20591         valid = valid && !this.invalidHandleTypes[nodeName];
20592         valid = valid && !this.invalidHandleIds[node.id];
20593
20594         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20595             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20596         }
20597
20598
20599         return valid;
20600
20601     },
20602
20603     /**
20604      * Create the array of horizontal tick marks if an interval was specified
20605      * in setXConstraint().
20606      * @method setXTicks
20607      * @private
20608      */
20609     setXTicks: function(iStartX, iTickSize) {
20610         this.xTicks = [];
20611         this.xTickSize = iTickSize;
20612
20613         var tickMap = {};
20614
20615         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20616             if (!tickMap[i]) {
20617                 this.xTicks[this.xTicks.length] = i;
20618                 tickMap[i] = true;
20619             }
20620         }
20621
20622         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20623             if (!tickMap[i]) {
20624                 this.xTicks[this.xTicks.length] = i;
20625                 tickMap[i] = true;
20626             }
20627         }
20628
20629         this.xTicks.sort(this.DDM.numericSort) ;
20630     },
20631
20632     /**
20633      * Create the array of vertical tick marks if an interval was specified in
20634      * setYConstraint().
20635      * @method setYTicks
20636      * @private
20637      */
20638     setYTicks: function(iStartY, iTickSize) {
20639         this.yTicks = [];
20640         this.yTickSize = iTickSize;
20641
20642         var tickMap = {};
20643
20644         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20645             if (!tickMap[i]) {
20646                 this.yTicks[this.yTicks.length] = i;
20647                 tickMap[i] = true;
20648             }
20649         }
20650
20651         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20652             if (!tickMap[i]) {
20653                 this.yTicks[this.yTicks.length] = i;
20654                 tickMap[i] = true;
20655             }
20656         }
20657
20658         this.yTicks.sort(this.DDM.numericSort) ;
20659     },
20660
20661     /**
20662      * By default, the element can be dragged any place on the screen.  Use
20663      * this method to limit the horizontal travel of the element.  Pass in
20664      * 0,0 for the parameters if you want to lock the drag to the y axis.
20665      * @method setXConstraint
20666      * @param {int} iLeft the number of pixels the element can move to the left
20667      * @param {int} iRight the number of pixels the element can move to the
20668      * right
20669      * @param {int} iTickSize optional parameter for specifying that the
20670      * element
20671      * should move iTickSize pixels at a time.
20672      */
20673     setXConstraint: function(iLeft, iRight, iTickSize) {
20674         this.leftConstraint = iLeft;
20675         this.rightConstraint = iRight;
20676
20677         this.minX = this.initPageX - iLeft;
20678         this.maxX = this.initPageX + iRight;
20679         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20680
20681         this.constrainX = true;
20682     },
20683
20684     /**
20685      * Clears any constraints applied to this instance.  Also clears ticks
20686      * since they can't exist independent of a constraint at this time.
20687      * @method clearConstraints
20688      */
20689     clearConstraints: function() {
20690         this.constrainX = false;
20691         this.constrainY = false;
20692         this.clearTicks();
20693     },
20694
20695     /**
20696      * Clears any tick interval defined for this instance
20697      * @method clearTicks
20698      */
20699     clearTicks: function() {
20700         this.xTicks = null;
20701         this.yTicks = null;
20702         this.xTickSize = 0;
20703         this.yTickSize = 0;
20704     },
20705
20706     /**
20707      * By default, the element can be dragged any place on the screen.  Set
20708      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20709      * parameters if you want to lock the drag to the x axis.
20710      * @method setYConstraint
20711      * @param {int} iUp the number of pixels the element can move up
20712      * @param {int} iDown the number of pixels the element can move down
20713      * @param {int} iTickSize optional parameter for specifying that the
20714      * element should move iTickSize pixels at a time.
20715      */
20716     setYConstraint: function(iUp, iDown, iTickSize) {
20717         this.topConstraint = iUp;
20718         this.bottomConstraint = iDown;
20719
20720         this.minY = this.initPageY - iUp;
20721         this.maxY = this.initPageY + iDown;
20722         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20723
20724         this.constrainY = true;
20725
20726     },
20727
20728     /**
20729      * resetConstraints must be called if you manually reposition a dd element.
20730      * @method resetConstraints
20731      * @param {boolean} maintainOffset
20732      */
20733     resetConstraints: function() {
20734
20735
20736         // Maintain offsets if necessary
20737         if (this.initPageX || this.initPageX === 0) {
20738             // figure out how much this thing has moved
20739             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20740             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20741
20742             this.setInitPosition(dx, dy);
20743
20744         // This is the first time we have detected the element's position
20745         } else {
20746             this.setInitPosition();
20747         }
20748
20749         if (this.constrainX) {
20750             this.setXConstraint( this.leftConstraint,
20751                                  this.rightConstraint,
20752                                  this.xTickSize        );
20753         }
20754
20755         if (this.constrainY) {
20756             this.setYConstraint( this.topConstraint,
20757                                  this.bottomConstraint,
20758                                  this.yTickSize         );
20759         }
20760     },
20761
20762     /**
20763      * Normally the drag element is moved pixel by pixel, but we can specify
20764      * that it move a number of pixels at a time.  This method resolves the
20765      * location when we have it set up like this.
20766      * @method getTick
20767      * @param {int} val where we want to place the object
20768      * @param {int[]} tickArray sorted array of valid points
20769      * @return {int} the closest tick
20770      * @private
20771      */
20772     getTick: function(val, tickArray) {
20773
20774         if (!tickArray) {
20775             // If tick interval is not defined, it is effectively 1 pixel,
20776             // so we return the value passed to us.
20777             return val;
20778         } else if (tickArray[0] >= val) {
20779             // The value is lower than the first tick, so we return the first
20780             // tick.
20781             return tickArray[0];
20782         } else {
20783             for (var i=0, len=tickArray.length; i<len; ++i) {
20784                 var next = i + 1;
20785                 if (tickArray[next] && tickArray[next] >= val) {
20786                     var diff1 = val - tickArray[i];
20787                     var diff2 = tickArray[next] - val;
20788                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20789                 }
20790             }
20791
20792             // The value is larger than the last tick, so we return the last
20793             // tick.
20794             return tickArray[tickArray.length - 1];
20795         }
20796     },
20797
20798     /**
20799      * toString method
20800      * @method toString
20801      * @return {string} string representation of the dd obj
20802      */
20803     toString: function() {
20804         return ("DragDrop " + this.id);
20805     }
20806
20807 });
20808
20809 })();
20810 /*
20811  * Based on:
20812  * Ext JS Library 1.1.1
20813  * Copyright(c) 2006-2007, Ext JS, LLC.
20814  *
20815  * Originally Released Under LGPL - original licence link has changed is not relivant.
20816  *
20817  * Fork - LGPL
20818  * <script type="text/javascript">
20819  */
20820
20821
20822 /**
20823  * The drag and drop utility provides a framework for building drag and drop
20824  * applications.  In addition to enabling drag and drop for specific elements,
20825  * the drag and drop elements are tracked by the manager class, and the
20826  * interactions between the various elements are tracked during the drag and
20827  * the implementing code is notified about these important moments.
20828  */
20829
20830 // Only load the library once.  Rewriting the manager class would orphan
20831 // existing drag and drop instances.
20832 if (!Roo.dd.DragDropMgr) {
20833
20834 /**
20835  * @class Roo.dd.DragDropMgr
20836  * DragDropMgr is a singleton that tracks the element interaction for
20837  * all DragDrop items in the window.  Generally, you will not call
20838  * this class directly, but it does have helper methods that could
20839  * be useful in your DragDrop implementations.
20840  * @static
20841  */
20842 Roo.dd.DragDropMgr = function() {
20843
20844     var Event = Roo.EventManager;
20845
20846     return {
20847
20848         /**
20849          * Two dimensional Array of registered DragDrop objects.  The first
20850          * dimension is the DragDrop item group, the second the DragDrop
20851          * object.
20852          * @property ids
20853          * @type {string: string}
20854          * @private
20855          * @static
20856          */
20857         ids: {},
20858
20859         /**
20860          * Array of element ids defined as drag handles.  Used to determine
20861          * if the element that generated the mousedown event is actually the
20862          * handle and not the html element itself.
20863          * @property handleIds
20864          * @type {string: string}
20865          * @private
20866          * @static
20867          */
20868         handleIds: {},
20869
20870         /**
20871          * the DragDrop object that is currently being dragged
20872          * @property dragCurrent
20873          * @type DragDrop
20874          * @private
20875          * @static
20876          **/
20877         dragCurrent: null,
20878
20879         /**
20880          * the DragDrop object(s) that are being hovered over
20881          * @property dragOvers
20882          * @type Array
20883          * @private
20884          * @static
20885          */
20886         dragOvers: {},
20887
20888         /**
20889          * the X distance between the cursor and the object being dragged
20890          * @property deltaX
20891          * @type int
20892          * @private
20893          * @static
20894          */
20895         deltaX: 0,
20896
20897         /**
20898          * the Y distance between the cursor and the object being dragged
20899          * @property deltaY
20900          * @type int
20901          * @private
20902          * @static
20903          */
20904         deltaY: 0,
20905
20906         /**
20907          * Flag to determine if we should prevent the default behavior of the
20908          * events we define. By default this is true, but this can be set to
20909          * false if you need the default behavior (not recommended)
20910          * @property preventDefault
20911          * @type boolean
20912          * @static
20913          */
20914         preventDefault: true,
20915
20916         /**
20917          * Flag to determine if we should stop the propagation of the events
20918          * we generate. This is true by default but you may want to set it to
20919          * false if the html element contains other features that require the
20920          * mouse click.
20921          * @property stopPropagation
20922          * @type boolean
20923          * @static
20924          */
20925         stopPropagation: true,
20926
20927         /**
20928          * Internal flag that is set to true when drag and drop has been
20929          * intialized
20930          * @property initialized
20931          * @private
20932          * @static
20933          */
20934         initalized: false,
20935
20936         /**
20937          * All drag and drop can be disabled.
20938          * @property locked
20939          * @private
20940          * @static
20941          */
20942         locked: false,
20943
20944         /**
20945          * Called the first time an element is registered.
20946          * @method init
20947          * @private
20948          * @static
20949          */
20950         init: function() {
20951             this.initialized = true;
20952         },
20953
20954         /**
20955          * In point mode, drag and drop interaction is defined by the
20956          * location of the cursor during the drag/drop
20957          * @property POINT
20958          * @type int
20959          * @static
20960          */
20961         POINT: 0,
20962
20963         /**
20964          * In intersect mode, drag and drop interactio nis defined by the
20965          * overlap of two or more drag and drop objects.
20966          * @property INTERSECT
20967          * @type int
20968          * @static
20969          */
20970         INTERSECT: 1,
20971
20972         /**
20973          * The current drag and drop mode.  Default: POINT
20974          * @property mode
20975          * @type int
20976          * @static
20977          */
20978         mode: 0,
20979
20980         /**
20981          * Runs method on all drag and drop objects
20982          * @method _execOnAll
20983          * @private
20984          * @static
20985          */
20986         _execOnAll: function(sMethod, args) {
20987             for (var i in this.ids) {
20988                 for (var j in this.ids[i]) {
20989                     var oDD = this.ids[i][j];
20990                     if (! this.isTypeOfDD(oDD)) {
20991                         continue;
20992                     }
20993                     oDD[sMethod].apply(oDD, args);
20994                 }
20995             }
20996         },
20997
20998         /**
20999          * Drag and drop initialization.  Sets up the global event handlers
21000          * @method _onLoad
21001          * @private
21002          * @static
21003          */
21004         _onLoad: function() {
21005
21006             this.init();
21007
21008             if (!Roo.isTouch) {
21009                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21010                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21011             }
21012             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21013             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21014             
21015             Event.on(window,   "unload",    this._onUnload, this, true);
21016             Event.on(window,   "resize",    this._onResize, this, true);
21017             // Event.on(window,   "mouseout",    this._test);
21018
21019         },
21020
21021         /**
21022          * Reset constraints on all drag and drop objs
21023          * @method _onResize
21024          * @private
21025          * @static
21026          */
21027         _onResize: function(e) {
21028             this._execOnAll("resetConstraints", []);
21029         },
21030
21031         /**
21032          * Lock all drag and drop functionality
21033          * @method lock
21034          * @static
21035          */
21036         lock: function() { this.locked = true; },
21037
21038         /**
21039          * Unlock all drag and drop functionality
21040          * @method unlock
21041          * @static
21042          */
21043         unlock: function() { this.locked = false; },
21044
21045         /**
21046          * Is drag and drop locked?
21047          * @method isLocked
21048          * @return {boolean} True if drag and drop is locked, false otherwise.
21049          * @static
21050          */
21051         isLocked: function() { return this.locked; },
21052
21053         /**
21054          * Location cache that is set for all drag drop objects when a drag is
21055          * initiated, cleared when the drag is finished.
21056          * @property locationCache
21057          * @private
21058          * @static
21059          */
21060         locationCache: {},
21061
21062         /**
21063          * Set useCache to false if you want to force object the lookup of each
21064          * drag and drop linked element constantly during a drag.
21065          * @property useCache
21066          * @type boolean
21067          * @static
21068          */
21069         useCache: true,
21070
21071         /**
21072          * The number of pixels that the mouse needs to move after the
21073          * mousedown before the drag is initiated.  Default=3;
21074          * @property clickPixelThresh
21075          * @type int
21076          * @static
21077          */
21078         clickPixelThresh: 3,
21079
21080         /**
21081          * The number of milliseconds after the mousedown event to initiate the
21082          * drag if we don't get a mouseup event. Default=1000
21083          * @property clickTimeThresh
21084          * @type int
21085          * @static
21086          */
21087         clickTimeThresh: 350,
21088
21089         /**
21090          * Flag that indicates that either the drag pixel threshold or the
21091          * mousdown time threshold has been met
21092          * @property dragThreshMet
21093          * @type boolean
21094          * @private
21095          * @static
21096          */
21097         dragThreshMet: false,
21098
21099         /**
21100          * Timeout used for the click time threshold
21101          * @property clickTimeout
21102          * @type Object
21103          * @private
21104          * @static
21105          */
21106         clickTimeout: null,
21107
21108         /**
21109          * The X position of the mousedown event stored for later use when a
21110          * drag threshold is met.
21111          * @property startX
21112          * @type int
21113          * @private
21114          * @static
21115          */
21116         startX: 0,
21117
21118         /**
21119          * The Y position of the mousedown event stored for later use when a
21120          * drag threshold is met.
21121          * @property startY
21122          * @type int
21123          * @private
21124          * @static
21125          */
21126         startY: 0,
21127
21128         /**
21129          * Each DragDrop instance must be registered with the DragDropMgr.
21130          * This is executed in DragDrop.init()
21131          * @method regDragDrop
21132          * @param {DragDrop} oDD the DragDrop object to register
21133          * @param {String} sGroup the name of the group this element belongs to
21134          * @static
21135          */
21136         regDragDrop: function(oDD, sGroup) {
21137             if (!this.initialized) { this.init(); }
21138
21139             if (!this.ids[sGroup]) {
21140                 this.ids[sGroup] = {};
21141             }
21142             this.ids[sGroup][oDD.id] = oDD;
21143         },
21144
21145         /**
21146          * Removes the supplied dd instance from the supplied group. Executed
21147          * by DragDrop.removeFromGroup, so don't call this function directly.
21148          * @method removeDDFromGroup
21149          * @private
21150          * @static
21151          */
21152         removeDDFromGroup: function(oDD, sGroup) {
21153             if (!this.ids[sGroup]) {
21154                 this.ids[sGroup] = {};
21155             }
21156
21157             var obj = this.ids[sGroup];
21158             if (obj && obj[oDD.id]) {
21159                 delete obj[oDD.id];
21160             }
21161         },
21162
21163         /**
21164          * Unregisters a drag and drop item.  This is executed in
21165          * DragDrop.unreg, use that method instead of calling this directly.
21166          * @method _remove
21167          * @private
21168          * @static
21169          */
21170         _remove: function(oDD) {
21171             for (var g in oDD.groups) {
21172                 if (g && this.ids[g][oDD.id]) {
21173                     delete this.ids[g][oDD.id];
21174                 }
21175             }
21176             delete this.handleIds[oDD.id];
21177         },
21178
21179         /**
21180          * Each DragDrop handle element must be registered.  This is done
21181          * automatically when executing DragDrop.setHandleElId()
21182          * @method regHandle
21183          * @param {String} sDDId the DragDrop id this element is a handle for
21184          * @param {String} sHandleId the id of the element that is the drag
21185          * handle
21186          * @static
21187          */
21188         regHandle: function(sDDId, sHandleId) {
21189             if (!this.handleIds[sDDId]) {
21190                 this.handleIds[sDDId] = {};
21191             }
21192             this.handleIds[sDDId][sHandleId] = sHandleId;
21193         },
21194
21195         /**
21196          * Utility function to determine if a given element has been
21197          * registered as a drag drop item.
21198          * @method isDragDrop
21199          * @param {String} id the element id to check
21200          * @return {boolean} true if this element is a DragDrop item,
21201          * false otherwise
21202          * @static
21203          */
21204         isDragDrop: function(id) {
21205             return ( this.getDDById(id) ) ? true : false;
21206         },
21207
21208         /**
21209          * Returns the drag and drop instances that are in all groups the
21210          * passed in instance belongs to.
21211          * @method getRelated
21212          * @param {DragDrop} p_oDD the obj to get related data for
21213          * @param {boolean} bTargetsOnly if true, only return targetable objs
21214          * @return {DragDrop[]} the related instances
21215          * @static
21216          */
21217         getRelated: function(p_oDD, bTargetsOnly) {
21218             var oDDs = [];
21219             for (var i in p_oDD.groups) {
21220                 for (j in this.ids[i]) {
21221                     var dd = this.ids[i][j];
21222                     if (! this.isTypeOfDD(dd)) {
21223                         continue;
21224                     }
21225                     if (!bTargetsOnly || dd.isTarget) {
21226                         oDDs[oDDs.length] = dd;
21227                     }
21228                 }
21229             }
21230
21231             return oDDs;
21232         },
21233
21234         /**
21235          * Returns true if the specified dd target is a legal target for
21236          * the specifice drag obj
21237          * @method isLegalTarget
21238          * @param {DragDrop} the drag obj
21239          * @param {DragDrop} the target
21240          * @return {boolean} true if the target is a legal target for the
21241          * dd obj
21242          * @static
21243          */
21244         isLegalTarget: function (oDD, oTargetDD) {
21245             var targets = this.getRelated(oDD, true);
21246             for (var i=0, len=targets.length;i<len;++i) {
21247                 if (targets[i].id == oTargetDD.id) {
21248                     return true;
21249                 }
21250             }
21251
21252             return false;
21253         },
21254
21255         /**
21256          * My goal is to be able to transparently determine if an object is
21257          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21258          * returns "object", oDD.constructor.toString() always returns
21259          * "DragDrop" and not the name of the subclass.  So for now it just
21260          * evaluates a well-known variable in DragDrop.
21261          * @method isTypeOfDD
21262          * @param {Object} the object to evaluate
21263          * @return {boolean} true if typeof oDD = DragDrop
21264          * @static
21265          */
21266         isTypeOfDD: function (oDD) {
21267             return (oDD && oDD.__ygDragDrop);
21268         },
21269
21270         /**
21271          * Utility function to determine if a given element has been
21272          * registered as a drag drop handle for the given Drag Drop object.
21273          * @method isHandle
21274          * @param {String} id the element id to check
21275          * @return {boolean} true if this element is a DragDrop handle, false
21276          * otherwise
21277          * @static
21278          */
21279         isHandle: function(sDDId, sHandleId) {
21280             return ( this.handleIds[sDDId] &&
21281                             this.handleIds[sDDId][sHandleId] );
21282         },
21283
21284         /**
21285          * Returns the DragDrop instance for a given id
21286          * @method getDDById
21287          * @param {String} id the id of the DragDrop object
21288          * @return {DragDrop} the drag drop object, null if it is not found
21289          * @static
21290          */
21291         getDDById: function(id) {
21292             for (var i in this.ids) {
21293                 if (this.ids[i][id]) {
21294                     return this.ids[i][id];
21295                 }
21296             }
21297             return null;
21298         },
21299
21300         /**
21301          * Fired after a registered DragDrop object gets the mousedown event.
21302          * Sets up the events required to track the object being dragged
21303          * @method handleMouseDown
21304          * @param {Event} e the event
21305          * @param oDD the DragDrop object being dragged
21306          * @private
21307          * @static
21308          */
21309         handleMouseDown: function(e, oDD) {
21310             if(Roo.QuickTips){
21311                 Roo.QuickTips.disable();
21312             }
21313             this.currentTarget = e.getTarget();
21314
21315             this.dragCurrent = oDD;
21316
21317             var el = oDD.getEl();
21318
21319             // track start position
21320             this.startX = e.getPageX();
21321             this.startY = e.getPageY();
21322
21323             this.deltaX = this.startX - el.offsetLeft;
21324             this.deltaY = this.startY - el.offsetTop;
21325
21326             this.dragThreshMet = false;
21327
21328             this.clickTimeout = setTimeout(
21329                     function() {
21330                         var DDM = Roo.dd.DDM;
21331                         DDM.startDrag(DDM.startX, DDM.startY);
21332                     },
21333                     this.clickTimeThresh );
21334         },
21335
21336         /**
21337          * Fired when either the drag pixel threshol or the mousedown hold
21338          * time threshold has been met.
21339          * @method startDrag
21340          * @param x {int} the X position of the original mousedown
21341          * @param y {int} the Y position of the original mousedown
21342          * @static
21343          */
21344         startDrag: function(x, y) {
21345             clearTimeout(this.clickTimeout);
21346             if (this.dragCurrent) {
21347                 this.dragCurrent.b4StartDrag(x, y);
21348                 this.dragCurrent.startDrag(x, y);
21349             }
21350             this.dragThreshMet = true;
21351         },
21352
21353         /**
21354          * Internal function to handle the mouseup event.  Will be invoked
21355          * from the context of the document.
21356          * @method handleMouseUp
21357          * @param {Event} e the event
21358          * @private
21359          * @static
21360          */
21361         handleMouseUp: function(e) {
21362
21363             if(Roo.QuickTips){
21364                 Roo.QuickTips.enable();
21365             }
21366             if (! this.dragCurrent) {
21367                 return;
21368             }
21369
21370             clearTimeout(this.clickTimeout);
21371
21372             if (this.dragThreshMet) {
21373                 this.fireEvents(e, true);
21374             } else {
21375             }
21376
21377             this.stopDrag(e);
21378
21379             this.stopEvent(e);
21380         },
21381
21382         /**
21383          * Utility to stop event propagation and event default, if these
21384          * features are turned on.
21385          * @method stopEvent
21386          * @param {Event} e the event as returned by this.getEvent()
21387          * @static
21388          */
21389         stopEvent: function(e){
21390             if(this.stopPropagation) {
21391                 e.stopPropagation();
21392             }
21393
21394             if (this.preventDefault) {
21395                 e.preventDefault();
21396             }
21397         },
21398
21399         /**
21400          * Internal function to clean up event handlers after the drag
21401          * operation is complete
21402          * @method stopDrag
21403          * @param {Event} e the event
21404          * @private
21405          * @static
21406          */
21407         stopDrag: function(e) {
21408             // Fire the drag end event for the item that was dragged
21409             if (this.dragCurrent) {
21410                 if (this.dragThreshMet) {
21411                     this.dragCurrent.b4EndDrag(e);
21412                     this.dragCurrent.endDrag(e);
21413                 }
21414
21415                 this.dragCurrent.onMouseUp(e);
21416             }
21417
21418             this.dragCurrent = null;
21419             this.dragOvers = {};
21420         },
21421
21422         /**
21423          * Internal function to handle the mousemove event.  Will be invoked
21424          * from the context of the html element.
21425          *
21426          * @TODO figure out what we can do about mouse events lost when the
21427          * user drags objects beyond the window boundary.  Currently we can
21428          * detect this in internet explorer by verifying that the mouse is
21429          * down during the mousemove event.  Firefox doesn't give us the
21430          * button state on the mousemove event.
21431          * @method handleMouseMove
21432          * @param {Event} e the event
21433          * @private
21434          * @static
21435          */
21436         handleMouseMove: function(e) {
21437             if (! this.dragCurrent) {
21438                 return true;
21439             }
21440
21441             // var button = e.which || e.button;
21442
21443             // check for IE mouseup outside of page boundary
21444             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21445                 this.stopEvent(e);
21446                 return this.handleMouseUp(e);
21447             }
21448
21449             if (!this.dragThreshMet) {
21450                 var diffX = Math.abs(this.startX - e.getPageX());
21451                 var diffY = Math.abs(this.startY - e.getPageY());
21452                 if (diffX > this.clickPixelThresh ||
21453                             diffY > this.clickPixelThresh) {
21454                     this.startDrag(this.startX, this.startY);
21455                 }
21456             }
21457
21458             if (this.dragThreshMet) {
21459                 this.dragCurrent.b4Drag(e);
21460                 this.dragCurrent.onDrag(e);
21461                 if(!this.dragCurrent.moveOnly){
21462                     this.fireEvents(e, false);
21463                 }
21464             }
21465
21466             this.stopEvent(e);
21467
21468             return true;
21469         },
21470
21471         /**
21472          * Iterates over all of the DragDrop elements to find ones we are
21473          * hovering over or dropping on
21474          * @method fireEvents
21475          * @param {Event} e the event
21476          * @param {boolean} isDrop is this a drop op or a mouseover op?
21477          * @private
21478          * @static
21479          */
21480         fireEvents: function(e, isDrop) {
21481             var dc = this.dragCurrent;
21482
21483             // If the user did the mouse up outside of the window, we could
21484             // get here even though we have ended the drag.
21485             if (!dc || dc.isLocked()) {
21486                 return;
21487             }
21488
21489             var pt = e.getPoint();
21490
21491             // cache the previous dragOver array
21492             var oldOvers = [];
21493
21494             var outEvts   = [];
21495             var overEvts  = [];
21496             var dropEvts  = [];
21497             var enterEvts = [];
21498
21499             // Check to see if the object(s) we were hovering over is no longer
21500             // being hovered over so we can fire the onDragOut event
21501             for (var i in this.dragOvers) {
21502
21503                 var ddo = this.dragOvers[i];
21504
21505                 if (! this.isTypeOfDD(ddo)) {
21506                     continue;
21507                 }
21508
21509                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21510                     outEvts.push( ddo );
21511                 }
21512
21513                 oldOvers[i] = true;
21514                 delete this.dragOvers[i];
21515             }
21516
21517             for (var sGroup in dc.groups) {
21518
21519                 if ("string" != typeof sGroup) {
21520                     continue;
21521                 }
21522
21523                 for (i in this.ids[sGroup]) {
21524                     var oDD = this.ids[sGroup][i];
21525                     if (! this.isTypeOfDD(oDD)) {
21526                         continue;
21527                     }
21528
21529                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21530                         if (this.isOverTarget(pt, oDD, this.mode)) {
21531                             // look for drop interactions
21532                             if (isDrop) {
21533                                 dropEvts.push( oDD );
21534                             // look for drag enter and drag over interactions
21535                             } else {
21536
21537                                 // initial drag over: dragEnter fires
21538                                 if (!oldOvers[oDD.id]) {
21539                                     enterEvts.push( oDD );
21540                                 // subsequent drag overs: dragOver fires
21541                                 } else {
21542                                     overEvts.push( oDD );
21543                                 }
21544
21545                                 this.dragOvers[oDD.id] = oDD;
21546                             }
21547                         }
21548                     }
21549                 }
21550             }
21551
21552             if (this.mode) {
21553                 if (outEvts.length) {
21554                     dc.b4DragOut(e, outEvts);
21555                     dc.onDragOut(e, outEvts);
21556                 }
21557
21558                 if (enterEvts.length) {
21559                     dc.onDragEnter(e, enterEvts);
21560                 }
21561
21562                 if (overEvts.length) {
21563                     dc.b4DragOver(e, overEvts);
21564                     dc.onDragOver(e, overEvts);
21565                 }
21566
21567                 if (dropEvts.length) {
21568                     dc.b4DragDrop(e, dropEvts);
21569                     dc.onDragDrop(e, dropEvts);
21570                 }
21571
21572             } else {
21573                 // fire dragout events
21574                 var len = 0;
21575                 for (i=0, len=outEvts.length; i<len; ++i) {
21576                     dc.b4DragOut(e, outEvts[i].id);
21577                     dc.onDragOut(e, outEvts[i].id);
21578                 }
21579
21580                 // fire enter events
21581                 for (i=0,len=enterEvts.length; i<len; ++i) {
21582                     // dc.b4DragEnter(e, oDD.id);
21583                     dc.onDragEnter(e, enterEvts[i].id);
21584                 }
21585
21586                 // fire over events
21587                 for (i=0,len=overEvts.length; i<len; ++i) {
21588                     dc.b4DragOver(e, overEvts[i].id);
21589                     dc.onDragOver(e, overEvts[i].id);
21590                 }
21591
21592                 // fire drop events
21593                 for (i=0, len=dropEvts.length; i<len; ++i) {
21594                     dc.b4DragDrop(e, dropEvts[i].id);
21595                     dc.onDragDrop(e, dropEvts[i].id);
21596                 }
21597
21598             }
21599
21600             // notify about a drop that did not find a target
21601             if (isDrop && !dropEvts.length) {
21602                 dc.onInvalidDrop(e);
21603             }
21604
21605         },
21606
21607         /**
21608          * Helper function for getting the best match from the list of drag
21609          * and drop objects returned by the drag and drop events when we are
21610          * in INTERSECT mode.  It returns either the first object that the
21611          * cursor is over, or the object that has the greatest overlap with
21612          * the dragged element.
21613          * @method getBestMatch
21614          * @param  {DragDrop[]} dds The array of drag and drop objects
21615          * targeted
21616          * @return {DragDrop}       The best single match
21617          * @static
21618          */
21619         getBestMatch: function(dds) {
21620             var winner = null;
21621             // Return null if the input is not what we expect
21622             //if (!dds || !dds.length || dds.length == 0) {
21623                // winner = null;
21624             // If there is only one item, it wins
21625             //} else if (dds.length == 1) {
21626
21627             var len = dds.length;
21628
21629             if (len == 1) {
21630                 winner = dds[0];
21631             } else {
21632                 // Loop through the targeted items
21633                 for (var i=0; i<len; ++i) {
21634                     var dd = dds[i];
21635                     // If the cursor is over the object, it wins.  If the
21636                     // cursor is over multiple matches, the first one we come
21637                     // to wins.
21638                     if (dd.cursorIsOver) {
21639                         winner = dd;
21640                         break;
21641                     // Otherwise the object with the most overlap wins
21642                     } else {
21643                         if (!winner ||
21644                             winner.overlap.getArea() < dd.overlap.getArea()) {
21645                             winner = dd;
21646                         }
21647                     }
21648                 }
21649             }
21650
21651             return winner;
21652         },
21653
21654         /**
21655          * Refreshes the cache of the top-left and bottom-right points of the
21656          * drag and drop objects in the specified group(s).  This is in the
21657          * format that is stored in the drag and drop instance, so typical
21658          * usage is:
21659          * <code>
21660          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21661          * </code>
21662          * Alternatively:
21663          * <code>
21664          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21665          * </code>
21666          * @TODO this really should be an indexed array.  Alternatively this
21667          * method could accept both.
21668          * @method refreshCache
21669          * @param {Object} groups an associative array of groups to refresh
21670          * @static
21671          */
21672         refreshCache: function(groups) {
21673             for (var sGroup in groups) {
21674                 if ("string" != typeof sGroup) {
21675                     continue;
21676                 }
21677                 for (var i in this.ids[sGroup]) {
21678                     var oDD = this.ids[sGroup][i];
21679
21680                     if (this.isTypeOfDD(oDD)) {
21681                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21682                         var loc = this.getLocation(oDD);
21683                         if (loc) {
21684                             this.locationCache[oDD.id] = loc;
21685                         } else {
21686                             delete this.locationCache[oDD.id];
21687                             // this will unregister the drag and drop object if
21688                             // the element is not in a usable state
21689                             // oDD.unreg();
21690                         }
21691                     }
21692                 }
21693             }
21694         },
21695
21696         /**
21697          * This checks to make sure an element exists and is in the DOM.  The
21698          * main purpose is to handle cases where innerHTML is used to remove
21699          * drag and drop objects from the DOM.  IE provides an 'unspecified
21700          * error' when trying to access the offsetParent of such an element
21701          * @method verifyEl
21702          * @param {HTMLElement} el the element to check
21703          * @return {boolean} true if the element looks usable
21704          * @static
21705          */
21706         verifyEl: function(el) {
21707             if (el) {
21708                 var parent;
21709                 if(Roo.isIE){
21710                     try{
21711                         parent = el.offsetParent;
21712                     }catch(e){}
21713                 }else{
21714                     parent = el.offsetParent;
21715                 }
21716                 if (parent) {
21717                     return true;
21718                 }
21719             }
21720
21721             return false;
21722         },
21723
21724         /**
21725          * Returns a Region object containing the drag and drop element's position
21726          * and size, including the padding configured for it
21727          * @method getLocation
21728          * @param {DragDrop} oDD the drag and drop object to get the
21729          *                       location for
21730          * @return {Roo.lib.Region} a Region object representing the total area
21731          *                             the element occupies, including any padding
21732          *                             the instance is configured for.
21733          * @static
21734          */
21735         getLocation: function(oDD) {
21736             if (! this.isTypeOfDD(oDD)) {
21737                 return null;
21738             }
21739
21740             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21741
21742             try {
21743                 pos= Roo.lib.Dom.getXY(el);
21744             } catch (e) { }
21745
21746             if (!pos) {
21747                 return null;
21748             }
21749
21750             x1 = pos[0];
21751             x2 = x1 + el.offsetWidth;
21752             y1 = pos[1];
21753             y2 = y1 + el.offsetHeight;
21754
21755             t = y1 - oDD.padding[0];
21756             r = x2 + oDD.padding[1];
21757             b = y2 + oDD.padding[2];
21758             l = x1 - oDD.padding[3];
21759
21760             return new Roo.lib.Region( t, r, b, l );
21761         },
21762
21763         /**
21764          * Checks the cursor location to see if it over the target
21765          * @method isOverTarget
21766          * @param {Roo.lib.Point} pt The point to evaluate
21767          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21768          * @return {boolean} true if the mouse is over the target
21769          * @private
21770          * @static
21771          */
21772         isOverTarget: function(pt, oTarget, intersect) {
21773             // use cache if available
21774             var loc = this.locationCache[oTarget.id];
21775             if (!loc || !this.useCache) {
21776                 loc = this.getLocation(oTarget);
21777                 this.locationCache[oTarget.id] = loc;
21778
21779             }
21780
21781             if (!loc) {
21782                 return false;
21783             }
21784
21785             oTarget.cursorIsOver = loc.contains( pt );
21786
21787             // DragDrop is using this as a sanity check for the initial mousedown
21788             // in this case we are done.  In POINT mode, if the drag obj has no
21789             // contraints, we are also done. Otherwise we need to evaluate the
21790             // location of the target as related to the actual location of the
21791             // dragged element.
21792             var dc = this.dragCurrent;
21793             if (!dc || !dc.getTargetCoord ||
21794                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21795                 return oTarget.cursorIsOver;
21796             }
21797
21798             oTarget.overlap = null;
21799
21800             // Get the current location of the drag element, this is the
21801             // location of the mouse event less the delta that represents
21802             // where the original mousedown happened on the element.  We
21803             // need to consider constraints and ticks as well.
21804             var pos = dc.getTargetCoord(pt.x, pt.y);
21805
21806             var el = dc.getDragEl();
21807             var curRegion = new Roo.lib.Region( pos.y,
21808                                                    pos.x + el.offsetWidth,
21809                                                    pos.y + el.offsetHeight,
21810                                                    pos.x );
21811
21812             var overlap = curRegion.intersect(loc);
21813
21814             if (overlap) {
21815                 oTarget.overlap = overlap;
21816                 return (intersect) ? true : oTarget.cursorIsOver;
21817             } else {
21818                 return false;
21819             }
21820         },
21821
21822         /**
21823          * unload event handler
21824          * @method _onUnload
21825          * @private
21826          * @static
21827          */
21828         _onUnload: function(e, me) {
21829             Roo.dd.DragDropMgr.unregAll();
21830         },
21831
21832         /**
21833          * Cleans up the drag and drop events and objects.
21834          * @method unregAll
21835          * @private
21836          * @static
21837          */
21838         unregAll: function() {
21839
21840             if (this.dragCurrent) {
21841                 this.stopDrag();
21842                 this.dragCurrent = null;
21843             }
21844
21845             this._execOnAll("unreg", []);
21846
21847             for (i in this.elementCache) {
21848                 delete this.elementCache[i];
21849             }
21850
21851             this.elementCache = {};
21852             this.ids = {};
21853         },
21854
21855         /**
21856          * A cache of DOM elements
21857          * @property elementCache
21858          * @private
21859          * @static
21860          */
21861         elementCache: {},
21862
21863         /**
21864          * Get the wrapper for the DOM element specified
21865          * @method getElWrapper
21866          * @param {String} id the id of the element to get
21867          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21868          * @private
21869          * @deprecated This wrapper isn't that useful
21870          * @static
21871          */
21872         getElWrapper: function(id) {
21873             var oWrapper = this.elementCache[id];
21874             if (!oWrapper || !oWrapper.el) {
21875                 oWrapper = this.elementCache[id] =
21876                     new this.ElementWrapper(Roo.getDom(id));
21877             }
21878             return oWrapper;
21879         },
21880
21881         /**
21882          * Returns the actual DOM element
21883          * @method getElement
21884          * @param {String} id the id of the elment to get
21885          * @return {Object} The element
21886          * @deprecated use Roo.getDom instead
21887          * @static
21888          */
21889         getElement: function(id) {
21890             return Roo.getDom(id);
21891         },
21892
21893         /**
21894          * Returns the style property for the DOM element (i.e.,
21895          * document.getElById(id).style)
21896          * @method getCss
21897          * @param {String} id the id of the elment to get
21898          * @return {Object} The style property of the element
21899          * @deprecated use Roo.getDom instead
21900          * @static
21901          */
21902         getCss: function(id) {
21903             var el = Roo.getDom(id);
21904             return (el) ? el.style : null;
21905         },
21906
21907         /**
21908          * Inner class for cached elements
21909          * @class DragDropMgr.ElementWrapper
21910          * @for DragDropMgr
21911          * @private
21912          * @deprecated
21913          */
21914         ElementWrapper: function(el) {
21915                 /**
21916                  * The element
21917                  * @property el
21918                  */
21919                 this.el = el || null;
21920                 /**
21921                  * The element id
21922                  * @property id
21923                  */
21924                 this.id = this.el && el.id;
21925                 /**
21926                  * A reference to the style property
21927                  * @property css
21928                  */
21929                 this.css = this.el && el.style;
21930             },
21931
21932         /**
21933          * Returns the X position of an html element
21934          * @method getPosX
21935          * @param el the element for which to get the position
21936          * @return {int} the X coordinate
21937          * @for DragDropMgr
21938          * @deprecated use Roo.lib.Dom.getX instead
21939          * @static
21940          */
21941         getPosX: function(el) {
21942             return Roo.lib.Dom.getX(el);
21943         },
21944
21945         /**
21946          * Returns the Y position of an html element
21947          * @method getPosY
21948          * @param el the element for which to get the position
21949          * @return {int} the Y coordinate
21950          * @deprecated use Roo.lib.Dom.getY instead
21951          * @static
21952          */
21953         getPosY: function(el) {
21954             return Roo.lib.Dom.getY(el);
21955         },
21956
21957         /**
21958          * Swap two nodes.  In IE, we use the native method, for others we
21959          * emulate the IE behavior
21960          * @method swapNode
21961          * @param n1 the first node to swap
21962          * @param n2 the other node to swap
21963          * @static
21964          */
21965         swapNode: function(n1, n2) {
21966             if (n1.swapNode) {
21967                 n1.swapNode(n2);
21968             } else {
21969                 var p = n2.parentNode;
21970                 var s = n2.nextSibling;
21971
21972                 if (s == n1) {
21973                     p.insertBefore(n1, n2);
21974                 } else if (n2 == n1.nextSibling) {
21975                     p.insertBefore(n2, n1);
21976                 } else {
21977                     n1.parentNode.replaceChild(n2, n1);
21978                     p.insertBefore(n1, s);
21979                 }
21980             }
21981         },
21982
21983         /**
21984          * Returns the current scroll position
21985          * @method getScroll
21986          * @private
21987          * @static
21988          */
21989         getScroll: function () {
21990             var t, l, dde=document.documentElement, db=document.body;
21991             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21992                 t = dde.scrollTop;
21993                 l = dde.scrollLeft;
21994             } else if (db) {
21995                 t = db.scrollTop;
21996                 l = db.scrollLeft;
21997             } else {
21998
21999             }
22000             return { top: t, left: l };
22001         },
22002
22003         /**
22004          * Returns the specified element style property
22005          * @method getStyle
22006          * @param {HTMLElement} el          the element
22007          * @param {string}      styleProp   the style property
22008          * @return {string} The value of the style property
22009          * @deprecated use Roo.lib.Dom.getStyle
22010          * @static
22011          */
22012         getStyle: function(el, styleProp) {
22013             return Roo.fly(el).getStyle(styleProp);
22014         },
22015
22016         /**
22017          * Gets the scrollTop
22018          * @method getScrollTop
22019          * @return {int} the document's scrollTop
22020          * @static
22021          */
22022         getScrollTop: function () { return this.getScroll().top; },
22023
22024         /**
22025          * Gets the scrollLeft
22026          * @method getScrollLeft
22027          * @return {int} the document's scrollTop
22028          * @static
22029          */
22030         getScrollLeft: function () { return this.getScroll().left; },
22031
22032         /**
22033          * Sets the x/y position of an element to the location of the
22034          * target element.
22035          * @method moveToEl
22036          * @param {HTMLElement} moveEl      The element to move
22037          * @param {HTMLElement} targetEl    The position reference element
22038          * @static
22039          */
22040         moveToEl: function (moveEl, targetEl) {
22041             var aCoord = Roo.lib.Dom.getXY(targetEl);
22042             Roo.lib.Dom.setXY(moveEl, aCoord);
22043         },
22044
22045         /**
22046          * Numeric array sort function
22047          * @method numericSort
22048          * @static
22049          */
22050         numericSort: function(a, b) { return (a - b); },
22051
22052         /**
22053          * Internal counter
22054          * @property _timeoutCount
22055          * @private
22056          * @static
22057          */
22058         _timeoutCount: 0,
22059
22060         /**
22061          * Trying to make the load order less important.  Without this we get
22062          * an error if this file is loaded before the Event Utility.
22063          * @method _addListeners
22064          * @private
22065          * @static
22066          */
22067         _addListeners: function() {
22068             var DDM = Roo.dd.DDM;
22069             if ( Roo.lib.Event && document ) {
22070                 DDM._onLoad();
22071             } else {
22072                 if (DDM._timeoutCount > 2000) {
22073                 } else {
22074                     setTimeout(DDM._addListeners, 10);
22075                     if (document && document.body) {
22076                         DDM._timeoutCount += 1;
22077                     }
22078                 }
22079             }
22080         },
22081
22082         /**
22083          * Recursively searches the immediate parent and all child nodes for
22084          * the handle element in order to determine wheter or not it was
22085          * clicked.
22086          * @method handleWasClicked
22087          * @param node the html element to inspect
22088          * @static
22089          */
22090         handleWasClicked: function(node, id) {
22091             if (this.isHandle(id, node.id)) {
22092                 return true;
22093             } else {
22094                 // check to see if this is a text node child of the one we want
22095                 var p = node.parentNode;
22096
22097                 while (p) {
22098                     if (this.isHandle(id, p.id)) {
22099                         return true;
22100                     } else {
22101                         p = p.parentNode;
22102                     }
22103                 }
22104             }
22105
22106             return false;
22107         }
22108
22109     };
22110
22111 }();
22112
22113 // shorter alias, save a few bytes
22114 Roo.dd.DDM = Roo.dd.DragDropMgr;
22115 Roo.dd.DDM._addListeners();
22116
22117 }/*
22118  * Based on:
22119  * Ext JS Library 1.1.1
22120  * Copyright(c) 2006-2007, Ext JS, LLC.
22121  *
22122  * Originally Released Under LGPL - original licence link has changed is not relivant.
22123  *
22124  * Fork - LGPL
22125  * <script type="text/javascript">
22126  */
22127
22128 /**
22129  * @class Roo.dd.DD
22130  * A DragDrop implementation where the linked element follows the
22131  * mouse cursor during a drag.
22132  * @extends Roo.dd.DragDrop
22133  * @constructor
22134  * @param {String} id the id of the linked element
22135  * @param {String} sGroup the group of related DragDrop items
22136  * @param {object} config an object containing configurable attributes
22137  *                Valid properties for DD:
22138  *                    scroll
22139  */
22140 Roo.dd.DD = function(id, sGroup, config) {
22141     if (id) {
22142         this.init(id, sGroup, config);
22143     }
22144 };
22145
22146 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22147
22148     /**
22149      * When set to true, the utility automatically tries to scroll the browser
22150      * window wehn a drag and drop element is dragged near the viewport boundary.
22151      * Defaults to true.
22152      * @property scroll
22153      * @type boolean
22154      */
22155     scroll: true,
22156
22157     /**
22158      * Sets the pointer offset to the distance between the linked element's top
22159      * left corner and the location the element was clicked
22160      * @method autoOffset
22161      * @param {int} iPageX the X coordinate of the click
22162      * @param {int} iPageY the Y coordinate of the click
22163      */
22164     autoOffset: function(iPageX, iPageY) {
22165         var x = iPageX - this.startPageX;
22166         var y = iPageY - this.startPageY;
22167         this.setDelta(x, y);
22168     },
22169
22170     /**
22171      * Sets the pointer offset.  You can call this directly to force the
22172      * offset to be in a particular location (e.g., pass in 0,0 to set it
22173      * to the center of the object)
22174      * @method setDelta
22175      * @param {int} iDeltaX the distance from the left
22176      * @param {int} iDeltaY the distance from the top
22177      */
22178     setDelta: function(iDeltaX, iDeltaY) {
22179         this.deltaX = iDeltaX;
22180         this.deltaY = iDeltaY;
22181     },
22182
22183     /**
22184      * Sets the drag element to the location of the mousedown or click event,
22185      * maintaining the cursor location relative to the location on the element
22186      * that was clicked.  Override this if you want to place the element in a
22187      * location other than where the cursor is.
22188      * @method setDragElPos
22189      * @param {int} iPageX the X coordinate of the mousedown or drag event
22190      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22191      */
22192     setDragElPos: function(iPageX, iPageY) {
22193         // the first time we do this, we are going to check to make sure
22194         // the element has css positioning
22195
22196         var el = this.getDragEl();
22197         this.alignElWithMouse(el, iPageX, iPageY);
22198     },
22199
22200     /**
22201      * Sets the element to the location of the mousedown or click event,
22202      * maintaining the cursor location relative to the location on the element
22203      * that was clicked.  Override this if you want to place the element in a
22204      * location other than where the cursor is.
22205      * @method alignElWithMouse
22206      * @param {HTMLElement} el the element to move
22207      * @param {int} iPageX the X coordinate of the mousedown or drag event
22208      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22209      */
22210     alignElWithMouse: function(el, iPageX, iPageY) {
22211         var oCoord = this.getTargetCoord(iPageX, iPageY);
22212         var fly = el.dom ? el : Roo.fly(el);
22213         if (!this.deltaSetXY) {
22214             var aCoord = [oCoord.x, oCoord.y];
22215             fly.setXY(aCoord);
22216             var newLeft = fly.getLeft(true);
22217             var newTop  = fly.getTop(true);
22218             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22219         } else {
22220             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22221         }
22222
22223         this.cachePosition(oCoord.x, oCoord.y);
22224         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22225         return oCoord;
22226     },
22227
22228     /**
22229      * Saves the most recent position so that we can reset the constraints and
22230      * tick marks on-demand.  We need to know this so that we can calculate the
22231      * number of pixels the element is offset from its original position.
22232      * @method cachePosition
22233      * @param iPageX the current x position (optional, this just makes it so we
22234      * don't have to look it up again)
22235      * @param iPageY the current y position (optional, this just makes it so we
22236      * don't have to look it up again)
22237      */
22238     cachePosition: function(iPageX, iPageY) {
22239         if (iPageX) {
22240             this.lastPageX = iPageX;
22241             this.lastPageY = iPageY;
22242         } else {
22243             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22244             this.lastPageX = aCoord[0];
22245             this.lastPageY = aCoord[1];
22246         }
22247     },
22248
22249     /**
22250      * Auto-scroll the window if the dragged object has been moved beyond the
22251      * visible window boundary.
22252      * @method autoScroll
22253      * @param {int} x the drag element's x position
22254      * @param {int} y the drag element's y position
22255      * @param {int} h the height of the drag element
22256      * @param {int} w the width of the drag element
22257      * @private
22258      */
22259     autoScroll: function(x, y, h, w) {
22260
22261         if (this.scroll) {
22262             // The client height
22263             var clientH = Roo.lib.Dom.getViewWidth();
22264
22265             // The client width
22266             var clientW = Roo.lib.Dom.getViewHeight();
22267
22268             // The amt scrolled down
22269             var st = this.DDM.getScrollTop();
22270
22271             // The amt scrolled right
22272             var sl = this.DDM.getScrollLeft();
22273
22274             // Location of the bottom of the element
22275             var bot = h + y;
22276
22277             // Location of the right of the element
22278             var right = w + x;
22279
22280             // The distance from the cursor to the bottom of the visible area,
22281             // adjusted so that we don't scroll if the cursor is beyond the
22282             // element drag constraints
22283             var toBot = (clientH + st - y - this.deltaY);
22284
22285             // The distance from the cursor to the right of the visible area
22286             var toRight = (clientW + sl - x - this.deltaX);
22287
22288
22289             // How close to the edge the cursor must be before we scroll
22290             // var thresh = (document.all) ? 100 : 40;
22291             var thresh = 40;
22292
22293             // How many pixels to scroll per autoscroll op.  This helps to reduce
22294             // clunky scrolling. IE is more sensitive about this ... it needs this
22295             // value to be higher.
22296             var scrAmt = (document.all) ? 80 : 30;
22297
22298             // Scroll down if we are near the bottom of the visible page and the
22299             // obj extends below the crease
22300             if ( bot > clientH && toBot < thresh ) {
22301                 window.scrollTo(sl, st + scrAmt);
22302             }
22303
22304             // Scroll up if the window is scrolled down and the top of the object
22305             // goes above the top border
22306             if ( y < st && st > 0 && y - st < thresh ) {
22307                 window.scrollTo(sl, st - scrAmt);
22308             }
22309
22310             // Scroll right if the obj is beyond the right border and the cursor is
22311             // near the border.
22312             if ( right > clientW && toRight < thresh ) {
22313                 window.scrollTo(sl + scrAmt, st);
22314             }
22315
22316             // Scroll left if the window has been scrolled to the right and the obj
22317             // extends past the left border
22318             if ( x < sl && sl > 0 && x - sl < thresh ) {
22319                 window.scrollTo(sl - scrAmt, st);
22320             }
22321         }
22322     },
22323
22324     /**
22325      * Finds the location the element should be placed if we want to move
22326      * it to where the mouse location less the click offset would place us.
22327      * @method getTargetCoord
22328      * @param {int} iPageX the X coordinate of the click
22329      * @param {int} iPageY the Y coordinate of the click
22330      * @return an object that contains the coordinates (Object.x and Object.y)
22331      * @private
22332      */
22333     getTargetCoord: function(iPageX, iPageY) {
22334
22335
22336         var x = iPageX - this.deltaX;
22337         var y = iPageY - this.deltaY;
22338
22339         if (this.constrainX) {
22340             if (x < this.minX) { x = this.minX; }
22341             if (x > this.maxX) { x = this.maxX; }
22342         }
22343
22344         if (this.constrainY) {
22345             if (y < this.minY) { y = this.minY; }
22346             if (y > this.maxY) { y = this.maxY; }
22347         }
22348
22349         x = this.getTick(x, this.xTicks);
22350         y = this.getTick(y, this.yTicks);
22351
22352
22353         return {x:x, y:y};
22354     },
22355
22356     /*
22357      * Sets up config options specific to this class. Overrides
22358      * Roo.dd.DragDrop, but all versions of this method through the
22359      * inheritance chain are called
22360      */
22361     applyConfig: function() {
22362         Roo.dd.DD.superclass.applyConfig.call(this);
22363         this.scroll = (this.config.scroll !== false);
22364     },
22365
22366     /*
22367      * Event that fires prior to the onMouseDown event.  Overrides
22368      * Roo.dd.DragDrop.
22369      */
22370     b4MouseDown: function(e) {
22371         // this.resetConstraints();
22372         this.autoOffset(e.getPageX(),
22373                             e.getPageY());
22374     },
22375
22376     /*
22377      * Event that fires prior to the onDrag event.  Overrides
22378      * Roo.dd.DragDrop.
22379      */
22380     b4Drag: function(e) {
22381         this.setDragElPos(e.getPageX(),
22382                             e.getPageY());
22383     },
22384
22385     toString: function() {
22386         return ("DD " + this.id);
22387     }
22388
22389     //////////////////////////////////////////////////////////////////////////
22390     // Debugging ygDragDrop events that can be overridden
22391     //////////////////////////////////////////////////////////////////////////
22392     /*
22393     startDrag: function(x, y) {
22394     },
22395
22396     onDrag: function(e) {
22397     },
22398
22399     onDragEnter: function(e, id) {
22400     },
22401
22402     onDragOver: function(e, id) {
22403     },
22404
22405     onDragOut: function(e, id) {
22406     },
22407
22408     onDragDrop: function(e, id) {
22409     },
22410
22411     endDrag: function(e) {
22412     }
22413
22414     */
22415
22416 });/*
22417  * Based on:
22418  * Ext JS Library 1.1.1
22419  * Copyright(c) 2006-2007, Ext JS, LLC.
22420  *
22421  * Originally Released Under LGPL - original licence link has changed is not relivant.
22422  *
22423  * Fork - LGPL
22424  * <script type="text/javascript">
22425  */
22426
22427 /**
22428  * @class Roo.dd.DDProxy
22429  * A DragDrop implementation that inserts an empty, bordered div into
22430  * the document that follows the cursor during drag operations.  At the time of
22431  * the click, the frame div is resized to the dimensions of the linked html
22432  * element, and moved to the exact location of the linked element.
22433  *
22434  * References to the "frame" element refer to the single proxy element that
22435  * was created to be dragged in place of all DDProxy elements on the
22436  * page.
22437  *
22438  * @extends Roo.dd.DD
22439  * @constructor
22440  * @param {String} id the id of the linked html element
22441  * @param {String} sGroup the group of related DragDrop objects
22442  * @param {object} config an object containing configurable attributes
22443  *                Valid properties for DDProxy in addition to those in DragDrop:
22444  *                   resizeFrame, centerFrame, dragElId
22445  */
22446 Roo.dd.DDProxy = function(id, sGroup, config) {
22447     if (id) {
22448         this.init(id, sGroup, config);
22449         this.initFrame();
22450     }
22451 };
22452
22453 /**
22454  * The default drag frame div id
22455  * @property Roo.dd.DDProxy.dragElId
22456  * @type String
22457  * @static
22458  */
22459 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22460
22461 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22462
22463     /**
22464      * By default we resize the drag frame to be the same size as the element
22465      * we want to drag (this is to get the frame effect).  We can turn it off
22466      * if we want a different behavior.
22467      * @property resizeFrame
22468      * @type boolean
22469      */
22470     resizeFrame: true,
22471
22472     /**
22473      * By default the frame is positioned exactly where the drag element is, so
22474      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22475      * you do not have constraints on the obj is to have the drag frame centered
22476      * around the cursor.  Set centerFrame to true for this effect.
22477      * @property centerFrame
22478      * @type boolean
22479      */
22480     centerFrame: false,
22481
22482     /**
22483      * Creates the proxy element if it does not yet exist
22484      * @method createFrame
22485      */
22486     createFrame: function() {
22487         var self = this;
22488         var body = document.body;
22489
22490         if (!body || !body.firstChild) {
22491             setTimeout( function() { self.createFrame(); }, 50 );
22492             return;
22493         }
22494
22495         var div = this.getDragEl();
22496
22497         if (!div) {
22498             div    = document.createElement("div");
22499             div.id = this.dragElId;
22500             var s  = div.style;
22501
22502             s.position   = "absolute";
22503             s.visibility = "hidden";
22504             s.cursor     = "move";
22505             s.border     = "2px solid #aaa";
22506             s.zIndex     = 999;
22507
22508             // appendChild can blow up IE if invoked prior to the window load event
22509             // while rendering a table.  It is possible there are other scenarios
22510             // that would cause this to happen as well.
22511             body.insertBefore(div, body.firstChild);
22512         }
22513     },
22514
22515     /**
22516      * Initialization for the drag frame element.  Must be called in the
22517      * constructor of all subclasses
22518      * @method initFrame
22519      */
22520     initFrame: function() {
22521         this.createFrame();
22522     },
22523
22524     applyConfig: function() {
22525         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22526
22527         this.resizeFrame = (this.config.resizeFrame !== false);
22528         this.centerFrame = (this.config.centerFrame);
22529         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22530     },
22531
22532     /**
22533      * Resizes the drag frame to the dimensions of the clicked object, positions
22534      * it over the object, and finally displays it
22535      * @method showFrame
22536      * @param {int} iPageX X click position
22537      * @param {int} iPageY Y click position
22538      * @private
22539      */
22540     showFrame: function(iPageX, iPageY) {
22541         var el = this.getEl();
22542         var dragEl = this.getDragEl();
22543         var s = dragEl.style;
22544
22545         this._resizeProxy();
22546
22547         if (this.centerFrame) {
22548             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22549                            Math.round(parseInt(s.height, 10)/2) );
22550         }
22551
22552         this.setDragElPos(iPageX, iPageY);
22553
22554         Roo.fly(dragEl).show();
22555     },
22556
22557     /**
22558      * The proxy is automatically resized to the dimensions of the linked
22559      * element when a drag is initiated, unless resizeFrame is set to false
22560      * @method _resizeProxy
22561      * @private
22562      */
22563     _resizeProxy: function() {
22564         if (this.resizeFrame) {
22565             var el = this.getEl();
22566             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22567         }
22568     },
22569
22570     // overrides Roo.dd.DragDrop
22571     b4MouseDown: function(e) {
22572         var x = e.getPageX();
22573         var y = e.getPageY();
22574         this.autoOffset(x, y);
22575         this.setDragElPos(x, y);
22576     },
22577
22578     // overrides Roo.dd.DragDrop
22579     b4StartDrag: function(x, y) {
22580         // show the drag frame
22581         this.showFrame(x, y);
22582     },
22583
22584     // overrides Roo.dd.DragDrop
22585     b4EndDrag: function(e) {
22586         Roo.fly(this.getDragEl()).hide();
22587     },
22588
22589     // overrides Roo.dd.DragDrop
22590     // By default we try to move the element to the last location of the frame.
22591     // This is so that the default behavior mirrors that of Roo.dd.DD.
22592     endDrag: function(e) {
22593
22594         var lel = this.getEl();
22595         var del = this.getDragEl();
22596
22597         // Show the drag frame briefly so we can get its position
22598         del.style.visibility = "";
22599
22600         this.beforeMove();
22601         // Hide the linked element before the move to get around a Safari
22602         // rendering bug.
22603         lel.style.visibility = "hidden";
22604         Roo.dd.DDM.moveToEl(lel, del);
22605         del.style.visibility = "hidden";
22606         lel.style.visibility = "";
22607
22608         this.afterDrag();
22609     },
22610
22611     beforeMove : function(){
22612
22613     },
22614
22615     afterDrag : function(){
22616
22617     },
22618
22619     toString: function() {
22620         return ("DDProxy " + this.id);
22621     }
22622
22623 });
22624 /*
22625  * Based on:
22626  * Ext JS Library 1.1.1
22627  * Copyright(c) 2006-2007, Ext JS, LLC.
22628  *
22629  * Originally Released Under LGPL - original licence link has changed is not relivant.
22630  *
22631  * Fork - LGPL
22632  * <script type="text/javascript">
22633  */
22634
22635  /**
22636  * @class Roo.dd.DDTarget
22637  * A DragDrop implementation that does not move, but can be a drop
22638  * target.  You would get the same result by simply omitting implementation
22639  * for the event callbacks, but this way we reduce the processing cost of the
22640  * event listener and the callbacks.
22641  * @extends Roo.dd.DragDrop
22642  * @constructor
22643  * @param {String} id the id of the element that is a drop target
22644  * @param {String} sGroup the group of related DragDrop objects
22645  * @param {object} config an object containing configurable attributes
22646  *                 Valid properties for DDTarget in addition to those in
22647  *                 DragDrop:
22648  *                    none
22649  */
22650 Roo.dd.DDTarget = function(id, sGroup, config) {
22651     if (id) {
22652         this.initTarget(id, sGroup, config);
22653     }
22654     if (config && (config.listeners || config.events)) { 
22655         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22656             listeners : config.listeners || {}, 
22657             events : config.events || {} 
22658         });    
22659     }
22660 };
22661
22662 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22663 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22664     toString: function() {
22665         return ("DDTarget " + this.id);
22666     }
22667 });
22668 /*
22669  * Based on:
22670  * Ext JS Library 1.1.1
22671  * Copyright(c) 2006-2007, Ext JS, LLC.
22672  *
22673  * Originally Released Under LGPL - original licence link has changed is not relivant.
22674  *
22675  * Fork - LGPL
22676  * <script type="text/javascript">
22677  */
22678  
22679
22680 /**
22681  * @class Roo.dd.ScrollManager
22682  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22683  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22684  * @static
22685  */
22686 Roo.dd.ScrollManager = function(){
22687     var ddm = Roo.dd.DragDropMgr;
22688     var els = {};
22689     var dragEl = null;
22690     var proc = {};
22691     
22692     
22693     
22694     var onStop = function(e){
22695         dragEl = null;
22696         clearProc();
22697     };
22698     
22699     var triggerRefresh = function(){
22700         if(ddm.dragCurrent){
22701              ddm.refreshCache(ddm.dragCurrent.groups);
22702         }
22703     };
22704     
22705     var doScroll = function(){
22706         if(ddm.dragCurrent){
22707             var dds = Roo.dd.ScrollManager;
22708             if(!dds.animate){
22709                 if(proc.el.scroll(proc.dir, dds.increment)){
22710                     triggerRefresh();
22711                 }
22712             }else{
22713                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22714             }
22715         }
22716     };
22717     
22718     var clearProc = function(){
22719         if(proc.id){
22720             clearInterval(proc.id);
22721         }
22722         proc.id = 0;
22723         proc.el = null;
22724         proc.dir = "";
22725     };
22726     
22727     var startProc = function(el, dir){
22728          Roo.log('scroll startproc');
22729         clearProc();
22730         proc.el = el;
22731         proc.dir = dir;
22732         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22733     };
22734     
22735     var onFire = function(e, isDrop){
22736        
22737         if(isDrop || !ddm.dragCurrent){ return; }
22738         var dds = Roo.dd.ScrollManager;
22739         if(!dragEl || dragEl != ddm.dragCurrent){
22740             dragEl = ddm.dragCurrent;
22741             // refresh regions on drag start
22742             dds.refreshCache();
22743         }
22744         
22745         var xy = Roo.lib.Event.getXY(e);
22746         var pt = new Roo.lib.Point(xy[0], xy[1]);
22747         for(var id in els){
22748             var el = els[id], r = el._region;
22749             if(r && r.contains(pt) && el.isScrollable()){
22750                 if(r.bottom - pt.y <= dds.thresh){
22751                     if(proc.el != el){
22752                         startProc(el, "down");
22753                     }
22754                     return;
22755                 }else if(r.right - pt.x <= dds.thresh){
22756                     if(proc.el != el){
22757                         startProc(el, "left");
22758                     }
22759                     return;
22760                 }else if(pt.y - r.top <= dds.thresh){
22761                     if(proc.el != el){
22762                         startProc(el, "up");
22763                     }
22764                     return;
22765                 }else if(pt.x - r.left <= dds.thresh){
22766                     if(proc.el != el){
22767                         startProc(el, "right");
22768                     }
22769                     return;
22770                 }
22771             }
22772         }
22773         clearProc();
22774     };
22775     
22776     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22777     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22778     
22779     return {
22780         /**
22781          * Registers new overflow element(s) to auto scroll
22782          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22783          */
22784         register : function(el){
22785             if(el instanceof Array){
22786                 for(var i = 0, len = el.length; i < len; i++) {
22787                         this.register(el[i]);
22788                 }
22789             }else{
22790                 el = Roo.get(el);
22791                 els[el.id] = el;
22792             }
22793             Roo.dd.ScrollManager.els = els;
22794         },
22795         
22796         /**
22797          * Unregisters overflow element(s) so they are no longer scrolled
22798          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22799          */
22800         unregister : function(el){
22801             if(el instanceof Array){
22802                 for(var i = 0, len = el.length; i < len; i++) {
22803                         this.unregister(el[i]);
22804                 }
22805             }else{
22806                 el = Roo.get(el);
22807                 delete els[el.id];
22808             }
22809         },
22810         
22811         /**
22812          * The number of pixels from the edge of a container the pointer needs to be to 
22813          * trigger scrolling (defaults to 25)
22814          * @type Number
22815          */
22816         thresh : 25,
22817         
22818         /**
22819          * The number of pixels to scroll in each scroll increment (defaults to 50)
22820          * @type Number
22821          */
22822         increment : 100,
22823         
22824         /**
22825          * The frequency of scrolls in milliseconds (defaults to 500)
22826          * @type Number
22827          */
22828         frequency : 500,
22829         
22830         /**
22831          * True to animate the scroll (defaults to true)
22832          * @type Boolean
22833          */
22834         animate: true,
22835         
22836         /**
22837          * The animation duration in seconds - 
22838          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22839          * @type Number
22840          */
22841         animDuration: .4,
22842         
22843         /**
22844          * Manually trigger a cache refresh.
22845          */
22846         refreshCache : function(){
22847             for(var id in els){
22848                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22849                     els[id]._region = els[id].getRegion();
22850                 }
22851             }
22852         }
22853     };
22854 }();/*
22855  * Based on:
22856  * Ext JS Library 1.1.1
22857  * Copyright(c) 2006-2007, Ext JS, LLC.
22858  *
22859  * Originally Released Under LGPL - original licence link has changed is not relivant.
22860  *
22861  * Fork - LGPL
22862  * <script type="text/javascript">
22863  */
22864  
22865
22866 /**
22867  * @class Roo.dd.Registry
22868  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22869  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22870  * @static
22871  */
22872 Roo.dd.Registry = function(){
22873     var elements = {}; 
22874     var handles = {}; 
22875     var autoIdSeed = 0;
22876
22877     var getId = function(el, autogen){
22878         if(typeof el == "string"){
22879             return el;
22880         }
22881         var id = el.id;
22882         if(!id && autogen !== false){
22883             id = "roodd-" + (++autoIdSeed);
22884             el.id = id;
22885         }
22886         return id;
22887     };
22888     
22889     return {
22890     /**
22891      * Register a drag drop element
22892      * @param {String|HTMLElement} element The id or DOM node to register
22893      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22894      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22895      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22896      * populated in the data object (if applicable):
22897      * <pre>
22898 Value      Description<br />
22899 ---------  ------------------------------------------<br />
22900 handles    Array of DOM nodes that trigger dragging<br />
22901            for the element being registered<br />
22902 isHandle   True if the element passed in triggers<br />
22903            dragging itself, else false
22904 </pre>
22905      */
22906         register : function(el, data){
22907             data = data || {};
22908             if(typeof el == "string"){
22909                 el = document.getElementById(el);
22910             }
22911             data.ddel = el;
22912             elements[getId(el)] = data;
22913             if(data.isHandle !== false){
22914                 handles[data.ddel.id] = data;
22915             }
22916             if(data.handles){
22917                 var hs = data.handles;
22918                 for(var i = 0, len = hs.length; i < len; i++){
22919                         handles[getId(hs[i])] = data;
22920                 }
22921             }
22922         },
22923
22924     /**
22925      * Unregister a drag drop element
22926      * @param {String|HTMLElement}  element The id or DOM node to unregister
22927      */
22928         unregister : function(el){
22929             var id = getId(el, false);
22930             var data = elements[id];
22931             if(data){
22932                 delete elements[id];
22933                 if(data.handles){
22934                     var hs = data.handles;
22935                     for(var i = 0, len = hs.length; i < len; i++){
22936                         delete handles[getId(hs[i], false)];
22937                     }
22938                 }
22939             }
22940         },
22941
22942     /**
22943      * Returns the handle registered for a DOM Node by id
22944      * @param {String|HTMLElement} id The DOM node or id to look up
22945      * @return {Object} handle The custom handle data
22946      */
22947         getHandle : function(id){
22948             if(typeof id != "string"){ // must be element?
22949                 id = id.id;
22950             }
22951             return handles[id];
22952         },
22953
22954     /**
22955      * Returns the handle that is registered for the DOM node that is the target of the event
22956      * @param {Event} e The event
22957      * @return {Object} handle The custom handle data
22958      */
22959         getHandleFromEvent : function(e){
22960             var t = Roo.lib.Event.getTarget(e);
22961             return t ? handles[t.id] : null;
22962         },
22963
22964     /**
22965      * Returns a custom data object that is registered for a DOM node by id
22966      * @param {String|HTMLElement} id The DOM node or id to look up
22967      * @return {Object} data The custom data
22968      */
22969         getTarget : function(id){
22970             if(typeof id != "string"){ // must be element?
22971                 id = id.id;
22972             }
22973             return elements[id];
22974         },
22975
22976     /**
22977      * Returns a custom data object that is registered for the DOM node that is the target of the event
22978      * @param {Event} e The event
22979      * @return {Object} data The custom data
22980      */
22981         getTargetFromEvent : function(e){
22982             var t = Roo.lib.Event.getTarget(e);
22983             return t ? elements[t.id] || handles[t.id] : null;
22984         }
22985     };
22986 }();/*
22987  * Based on:
22988  * Ext JS Library 1.1.1
22989  * Copyright(c) 2006-2007, Ext JS, LLC.
22990  *
22991  * Originally Released Under LGPL - original licence link has changed is not relivant.
22992  *
22993  * Fork - LGPL
22994  * <script type="text/javascript">
22995  */
22996  
22997
22998 /**
22999  * @class Roo.dd.StatusProxy
23000  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23001  * default drag proxy used by all Roo.dd components.
23002  * @constructor
23003  * @param {Object} config
23004  */
23005 Roo.dd.StatusProxy = function(config){
23006     Roo.apply(this, config);
23007     this.id = this.id || Roo.id();
23008     this.el = new Roo.Layer({
23009         dh: {
23010             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23011                 {tag: "div", cls: "x-dd-drop-icon"},
23012                 {tag: "div", cls: "x-dd-drag-ghost"}
23013             ]
23014         }, 
23015         shadow: !config || config.shadow !== false
23016     });
23017     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23018     this.dropStatus = this.dropNotAllowed;
23019 };
23020
23021 Roo.dd.StatusProxy.prototype = {
23022     /**
23023      * @cfg {String} dropAllowed
23024      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23025      */
23026     dropAllowed : "x-dd-drop-ok",
23027     /**
23028      * @cfg {String} dropNotAllowed
23029      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23030      */
23031     dropNotAllowed : "x-dd-drop-nodrop",
23032
23033     /**
23034      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23035      * over the current target element.
23036      * @param {String} cssClass The css class for the new drop status indicator image
23037      */
23038     setStatus : function(cssClass){
23039         cssClass = cssClass || this.dropNotAllowed;
23040         if(this.dropStatus != cssClass){
23041             this.el.replaceClass(this.dropStatus, cssClass);
23042             this.dropStatus = cssClass;
23043         }
23044     },
23045
23046     /**
23047      * Resets the status indicator to the default dropNotAllowed value
23048      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23049      */
23050     reset : function(clearGhost){
23051         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23052         this.dropStatus = this.dropNotAllowed;
23053         if(clearGhost){
23054             this.ghost.update("");
23055         }
23056     },
23057
23058     /**
23059      * Updates the contents of the ghost element
23060      * @param {String} html The html that will replace the current innerHTML of the ghost element
23061      */
23062     update : function(html){
23063         if(typeof html == "string"){
23064             this.ghost.update(html);
23065         }else{
23066             this.ghost.update("");
23067             html.style.margin = "0";
23068             this.ghost.dom.appendChild(html);
23069         }
23070         // ensure float = none set?? cant remember why though.
23071         var el = this.ghost.dom.firstChild;
23072                 if(el){
23073                         Roo.fly(el).setStyle('float', 'none');
23074                 }
23075     },
23076     
23077     /**
23078      * Returns the underlying proxy {@link Roo.Layer}
23079      * @return {Roo.Layer} el
23080     */
23081     getEl : function(){
23082         return this.el;
23083     },
23084
23085     /**
23086      * Returns the ghost element
23087      * @return {Roo.Element} el
23088      */
23089     getGhost : function(){
23090         return this.ghost;
23091     },
23092
23093     /**
23094      * Hides the proxy
23095      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23096      */
23097     hide : function(clear){
23098         this.el.hide();
23099         if(clear){
23100             this.reset(true);
23101         }
23102     },
23103
23104     /**
23105      * Stops the repair animation if it's currently running
23106      */
23107     stop : function(){
23108         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23109             this.anim.stop();
23110         }
23111     },
23112
23113     /**
23114      * Displays this proxy
23115      */
23116     show : function(){
23117         this.el.show();
23118     },
23119
23120     /**
23121      * Force the Layer to sync its shadow and shim positions to the element
23122      */
23123     sync : function(){
23124         this.el.sync();
23125     },
23126
23127     /**
23128      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23129      * invalid drop operation by the item being dragged.
23130      * @param {Array} xy The XY position of the element ([x, y])
23131      * @param {Function} callback The function to call after the repair is complete
23132      * @param {Object} scope The scope in which to execute the callback
23133      */
23134     repair : function(xy, callback, scope){
23135         this.callback = callback;
23136         this.scope = scope;
23137         if(xy && this.animRepair !== false){
23138             this.el.addClass("x-dd-drag-repair");
23139             this.el.hideUnders(true);
23140             this.anim = this.el.shift({
23141                 duration: this.repairDuration || .5,
23142                 easing: 'easeOut',
23143                 xy: xy,
23144                 stopFx: true,
23145                 callback: this.afterRepair,
23146                 scope: this
23147             });
23148         }else{
23149             this.afterRepair();
23150         }
23151     },
23152
23153     // private
23154     afterRepair : function(){
23155         this.hide(true);
23156         if(typeof this.callback == "function"){
23157             this.callback.call(this.scope || this);
23158         }
23159         this.callback = null;
23160         this.scope = null;
23161     }
23162 };/*
23163  * Based on:
23164  * Ext JS Library 1.1.1
23165  * Copyright(c) 2006-2007, Ext JS, LLC.
23166  *
23167  * Originally Released Under LGPL - original licence link has changed is not relivant.
23168  *
23169  * Fork - LGPL
23170  * <script type="text/javascript">
23171  */
23172
23173 /**
23174  * @class Roo.dd.DragSource
23175  * @extends Roo.dd.DDProxy
23176  * A simple class that provides the basic implementation needed to make any element draggable.
23177  * @constructor
23178  * @param {String/HTMLElement/Element} el The container element
23179  * @param {Object} config
23180  */
23181 Roo.dd.DragSource = function(el, config){
23182     this.el = Roo.get(el);
23183     this.dragData = {};
23184     
23185     Roo.apply(this, config);
23186     
23187     if(!this.proxy){
23188         this.proxy = new Roo.dd.StatusProxy();
23189     }
23190
23191     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23192           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23193     
23194     this.dragging = false;
23195 };
23196
23197 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23198     /**
23199      * @cfg {String} dropAllowed
23200      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23201      */
23202     dropAllowed : "x-dd-drop-ok",
23203     /**
23204      * @cfg {String} dropNotAllowed
23205      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23206      */
23207     dropNotAllowed : "x-dd-drop-nodrop",
23208
23209     /**
23210      * Returns the data object associated with this drag source
23211      * @return {Object} data An object containing arbitrary data
23212      */
23213     getDragData : function(e){
23214         return this.dragData;
23215     },
23216
23217     // private
23218     onDragEnter : function(e, id){
23219         var target = Roo.dd.DragDropMgr.getDDById(id);
23220         this.cachedTarget = target;
23221         if(this.beforeDragEnter(target, e, id) !== false){
23222             if(target.isNotifyTarget){
23223                 var status = target.notifyEnter(this, e, this.dragData);
23224                 this.proxy.setStatus(status);
23225             }else{
23226                 this.proxy.setStatus(this.dropAllowed);
23227             }
23228             
23229             if(this.afterDragEnter){
23230                 /**
23231                  * An empty function by default, but provided so that you can perform a custom action
23232                  * when the dragged item enters the drop target by providing an implementation.
23233                  * @param {Roo.dd.DragDrop} target The drop target
23234                  * @param {Event} e The event object
23235                  * @param {String} id The id of the dragged element
23236                  * @method afterDragEnter
23237                  */
23238                 this.afterDragEnter(target, e, id);
23239             }
23240         }
23241     },
23242
23243     /**
23244      * An empty function by default, but provided so that you can perform a custom action
23245      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23246      * @param {Roo.dd.DragDrop} target The drop target
23247      * @param {Event} e The event object
23248      * @param {String} id The id of the dragged element
23249      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23250      */
23251     beforeDragEnter : function(target, e, id){
23252         return true;
23253     },
23254
23255     // private
23256     alignElWithMouse: function() {
23257         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23258         this.proxy.sync();
23259     },
23260
23261     // private
23262     onDragOver : function(e, id){
23263         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23264         if(this.beforeDragOver(target, e, id) !== false){
23265             if(target.isNotifyTarget){
23266                 var status = target.notifyOver(this, e, this.dragData);
23267                 this.proxy.setStatus(status);
23268             }
23269
23270             if(this.afterDragOver){
23271                 /**
23272                  * An empty function by default, but provided so that you can perform a custom action
23273                  * while the dragged item is over the drop target by providing an implementation.
23274                  * @param {Roo.dd.DragDrop} target The drop target
23275                  * @param {Event} e The event object
23276                  * @param {String} id The id of the dragged element
23277                  * @method afterDragOver
23278                  */
23279                 this.afterDragOver(target, e, id);
23280             }
23281         }
23282     },
23283
23284     /**
23285      * An empty function by default, but provided so that you can perform a custom action
23286      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23287      * @param {Roo.dd.DragDrop} target The drop target
23288      * @param {Event} e The event object
23289      * @param {String} id The id of the dragged element
23290      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23291      */
23292     beforeDragOver : function(target, e, id){
23293         return true;
23294     },
23295
23296     // private
23297     onDragOut : function(e, id){
23298         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23299         if(this.beforeDragOut(target, e, id) !== false){
23300             if(target.isNotifyTarget){
23301                 target.notifyOut(this, e, this.dragData);
23302             }
23303             this.proxy.reset();
23304             if(this.afterDragOut){
23305                 /**
23306                  * An empty function by default, but provided so that you can perform a custom action
23307                  * after the dragged item is dragged out of the target without dropping.
23308                  * @param {Roo.dd.DragDrop} target The drop target
23309                  * @param {Event} e The event object
23310                  * @param {String} id The id of the dragged element
23311                  * @method afterDragOut
23312                  */
23313                 this.afterDragOut(target, e, id);
23314             }
23315         }
23316         this.cachedTarget = null;
23317     },
23318
23319     /**
23320      * An empty function by default, but provided so that you can perform a custom action before the dragged
23321      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23322      * @param {Roo.dd.DragDrop} target The drop target
23323      * @param {Event} e The event object
23324      * @param {String} id The id of the dragged element
23325      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23326      */
23327     beforeDragOut : function(target, e, id){
23328         return true;
23329     },
23330     
23331     // private
23332     onDragDrop : function(e, id){
23333         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23334         if(this.beforeDragDrop(target, e, id) !== false){
23335             if(target.isNotifyTarget){
23336                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23337                     this.onValidDrop(target, e, id);
23338                 }else{
23339                     this.onInvalidDrop(target, e, id);
23340                 }
23341             }else{
23342                 this.onValidDrop(target, e, id);
23343             }
23344             
23345             if(this.afterDragDrop){
23346                 /**
23347                  * An empty function by default, but provided so that you can perform a custom action
23348                  * after a valid drag drop has occurred by providing an implementation.
23349                  * @param {Roo.dd.DragDrop} target The drop target
23350                  * @param {Event} e The event object
23351                  * @param {String} id The id of the dropped element
23352                  * @method afterDragDrop
23353                  */
23354                 this.afterDragDrop(target, e, id);
23355             }
23356         }
23357         delete this.cachedTarget;
23358     },
23359
23360     /**
23361      * An empty function by default, but provided so that you can perform a custom action before the dragged
23362      * item is dropped onto the target and optionally cancel the onDragDrop.
23363      * @param {Roo.dd.DragDrop} target The drop target
23364      * @param {Event} e The event object
23365      * @param {String} id The id of the dragged element
23366      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23367      */
23368     beforeDragDrop : function(target, e, id){
23369         return true;
23370     },
23371
23372     // private
23373     onValidDrop : function(target, e, id){
23374         this.hideProxy();
23375         if(this.afterValidDrop){
23376             /**
23377              * An empty function by default, but provided so that you can perform a custom action
23378              * after a valid drop has occurred by providing an implementation.
23379              * @param {Object} target The target DD 
23380              * @param {Event} e The event object
23381              * @param {String} id The id of the dropped element
23382              * @method afterInvalidDrop
23383              */
23384             this.afterValidDrop(target, e, id);
23385         }
23386     },
23387
23388     // private
23389     getRepairXY : function(e, data){
23390         return this.el.getXY();  
23391     },
23392
23393     // private
23394     onInvalidDrop : function(target, e, id){
23395         this.beforeInvalidDrop(target, e, id);
23396         if(this.cachedTarget){
23397             if(this.cachedTarget.isNotifyTarget){
23398                 this.cachedTarget.notifyOut(this, e, this.dragData);
23399             }
23400             this.cacheTarget = null;
23401         }
23402         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23403
23404         if(this.afterInvalidDrop){
23405             /**
23406              * An empty function by default, but provided so that you can perform a custom action
23407              * after an invalid drop has occurred by providing an implementation.
23408              * @param {Event} e The event object
23409              * @param {String} id The id of the dropped element
23410              * @method afterInvalidDrop
23411              */
23412             this.afterInvalidDrop(e, id);
23413         }
23414     },
23415
23416     // private
23417     afterRepair : function(){
23418         if(Roo.enableFx){
23419             this.el.highlight(this.hlColor || "c3daf9");
23420         }
23421         this.dragging = false;
23422     },
23423
23424     /**
23425      * An empty function by default, but provided so that you can perform a custom action after an invalid
23426      * drop has occurred.
23427      * @param {Roo.dd.DragDrop} target The drop target
23428      * @param {Event} e The event object
23429      * @param {String} id The id of the dragged element
23430      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23431      */
23432     beforeInvalidDrop : function(target, e, id){
23433         return true;
23434     },
23435
23436     // private
23437     handleMouseDown : function(e){
23438         if(this.dragging) {
23439             return;
23440         }
23441         var data = this.getDragData(e);
23442         if(data && this.onBeforeDrag(data, e) !== false){
23443             this.dragData = data;
23444             this.proxy.stop();
23445             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23446         } 
23447     },
23448
23449     /**
23450      * An empty function by default, but provided so that you can perform a custom action before the initial
23451      * drag event begins and optionally cancel it.
23452      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23453      * @param {Event} e The event object
23454      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23455      */
23456     onBeforeDrag : function(data, e){
23457         return true;
23458     },
23459
23460     /**
23461      * An empty function by default, but provided so that you can perform a custom action once the initial
23462      * drag event has begun.  The drag cannot be canceled from this function.
23463      * @param {Number} x The x position of the click on the dragged object
23464      * @param {Number} y The y position of the click on the dragged object
23465      */
23466     onStartDrag : Roo.emptyFn,
23467
23468     // private - YUI override
23469     startDrag : function(x, y){
23470         this.proxy.reset();
23471         this.dragging = true;
23472         this.proxy.update("");
23473         this.onInitDrag(x, y);
23474         this.proxy.show();
23475     },
23476
23477     // private
23478     onInitDrag : function(x, y){
23479         var clone = this.el.dom.cloneNode(true);
23480         clone.id = Roo.id(); // prevent duplicate ids
23481         this.proxy.update(clone);
23482         this.onStartDrag(x, y);
23483         return true;
23484     },
23485
23486     /**
23487      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23488      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23489      */
23490     getProxy : function(){
23491         return this.proxy;  
23492     },
23493
23494     /**
23495      * Hides the drag source's {@link Roo.dd.StatusProxy}
23496      */
23497     hideProxy : function(){
23498         this.proxy.hide();  
23499         this.proxy.reset(true);
23500         this.dragging = false;
23501     },
23502
23503     // private
23504     triggerCacheRefresh : function(){
23505         Roo.dd.DDM.refreshCache(this.groups);
23506     },
23507
23508     // private - override to prevent hiding
23509     b4EndDrag: function(e) {
23510     },
23511
23512     // private - override to prevent moving
23513     endDrag : function(e){
23514         this.onEndDrag(this.dragData, e);
23515     },
23516
23517     // private
23518     onEndDrag : function(data, e){
23519     },
23520     
23521     // private - pin to cursor
23522     autoOffset : function(x, y) {
23523         this.setDelta(-12, -20);
23524     }    
23525 });/*
23526  * Based on:
23527  * Ext JS Library 1.1.1
23528  * Copyright(c) 2006-2007, Ext JS, LLC.
23529  *
23530  * Originally Released Under LGPL - original licence link has changed is not relivant.
23531  *
23532  * Fork - LGPL
23533  * <script type="text/javascript">
23534  */
23535
23536
23537 /**
23538  * @class Roo.dd.DropTarget
23539  * @extends Roo.dd.DDTarget
23540  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23541  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23542  * @constructor
23543  * @param {String/HTMLElement/Element} el The container element
23544  * @param {Object} config
23545  */
23546 Roo.dd.DropTarget = function(el, config){
23547     this.el = Roo.get(el);
23548     
23549     var listeners = false; ;
23550     if (config && config.listeners) {
23551         listeners= config.listeners;
23552         delete config.listeners;
23553     }
23554     Roo.apply(this, config);
23555     
23556     if(this.containerScroll){
23557         Roo.dd.ScrollManager.register(this.el);
23558     }
23559     this.addEvents( {
23560          /**
23561          * @scope Roo.dd.DropTarget
23562          */
23563          
23564          /**
23565          * @event enter
23566          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23567          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23568          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23569          * 
23570          * IMPORTANT : it should set  this.valid to true|false
23571          * 
23572          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23573          * @param {Event} e The event
23574          * @param {Object} data An object containing arbitrary data supplied by the drag source
23575          */
23576         "enter" : true,
23577         
23578          /**
23579          * @event over
23580          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23581          * This method will be called on every mouse movement while the drag source is over the drop target.
23582          * This default implementation simply returns the dropAllowed config value.
23583          * 
23584          * IMPORTANT : it should set  this.valid to true|false
23585          * 
23586          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23587          * @param {Event} e The event
23588          * @param {Object} data An object containing arbitrary data supplied by the drag source
23589          
23590          */
23591         "over" : true,
23592         /**
23593          * @event out
23594          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23595          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23596          * overClass (if any) from the drop element.
23597          * 
23598          * 
23599          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23600          * @param {Event} e The event
23601          * @param {Object} data An object containing arbitrary data supplied by the drag source
23602          */
23603          "out" : true,
23604          
23605         /**
23606          * @event drop
23607          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23608          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23609          * implementation that does something to process the drop event and returns true so that the drag source's
23610          * repair action does not run.
23611          * 
23612          * IMPORTANT : it should set this.success
23613          * 
23614          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23615          * @param {Event} e The event
23616          * @param {Object} data An object containing arbitrary data supplied by the drag source
23617         */
23618          "drop" : true
23619     });
23620             
23621      
23622     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23623         this.el.dom, 
23624         this.ddGroup || this.group,
23625         {
23626             isTarget: true,
23627             listeners : listeners || {} 
23628            
23629         
23630         }
23631     );
23632
23633 };
23634
23635 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23636     /**
23637      * @cfg {String} overClass
23638      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23639      */
23640      /**
23641      * @cfg {String} ddGroup
23642      * The drag drop group to handle drop events for
23643      */
23644      
23645     /**
23646      * @cfg {String} dropAllowed
23647      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23648      */
23649     dropAllowed : "x-dd-drop-ok",
23650     /**
23651      * @cfg {String} dropNotAllowed
23652      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23653      */
23654     dropNotAllowed : "x-dd-drop-nodrop",
23655     /**
23656      * @cfg {boolean} success
23657      * set this after drop listener.. 
23658      */
23659     success : false,
23660     /**
23661      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23662      * if the drop point is valid for over/enter..
23663      */
23664     valid : false,
23665     // private
23666     isTarget : true,
23667
23668     // private
23669     isNotifyTarget : true,
23670     
23671     /**
23672      * @hide
23673      */
23674     notifyEnter : function(dd, e, data)
23675     {
23676         this.valid = true;
23677         this.fireEvent('enter', dd, e, data);
23678         if(this.overClass){
23679             this.el.addClass(this.overClass);
23680         }
23681         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23682             this.valid ? this.dropAllowed : this.dropNotAllowed
23683         );
23684     },
23685
23686     /**
23687      * @hide
23688      */
23689     notifyOver : function(dd, e, data)
23690     {
23691         this.valid = true;
23692         this.fireEvent('over', dd, e, data);
23693         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23694             this.valid ? this.dropAllowed : this.dropNotAllowed
23695         );
23696     },
23697
23698     /**
23699      * @hide
23700      */
23701     notifyOut : function(dd, e, data)
23702     {
23703         this.fireEvent('out', dd, e, data);
23704         if(this.overClass){
23705             this.el.removeClass(this.overClass);
23706         }
23707     },
23708
23709     /**
23710      * @hide
23711      */
23712     notifyDrop : function(dd, e, data)
23713     {
23714         this.success = false;
23715         this.fireEvent('drop', dd, e, data);
23716         return this.success;
23717     }
23718 });/*
23719  * Based on:
23720  * Ext JS Library 1.1.1
23721  * Copyright(c) 2006-2007, Ext JS, LLC.
23722  *
23723  * Originally Released Under LGPL - original licence link has changed is not relivant.
23724  *
23725  * Fork - LGPL
23726  * <script type="text/javascript">
23727  */
23728
23729
23730 /**
23731  * @class Roo.dd.DragZone
23732  * @extends Roo.dd.DragSource
23733  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23734  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23735  * @constructor
23736  * @param {String/HTMLElement/Element} el The container element
23737  * @param {Object} config
23738  */
23739 Roo.dd.DragZone = function(el, config){
23740     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23741     if(this.containerScroll){
23742         Roo.dd.ScrollManager.register(this.el);
23743     }
23744 };
23745
23746 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23747     /**
23748      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23749      * for auto scrolling during drag operations.
23750      */
23751     /**
23752      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23753      * method after a failed drop (defaults to "c3daf9" - light blue)
23754      */
23755
23756     /**
23757      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23758      * for a valid target to drag based on the mouse down. Override this method
23759      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23760      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23761      * @param {EventObject} e The mouse down event
23762      * @return {Object} The dragData
23763      */
23764     getDragData : function(e){
23765         return Roo.dd.Registry.getHandleFromEvent(e);
23766     },
23767     
23768     /**
23769      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23770      * this.dragData.ddel
23771      * @param {Number} x The x position of the click on the dragged object
23772      * @param {Number} y The y position of the click on the dragged object
23773      * @return {Boolean} true to continue the drag, false to cancel
23774      */
23775     onInitDrag : function(x, y){
23776         this.proxy.update(this.dragData.ddel.cloneNode(true));
23777         this.onStartDrag(x, y);
23778         return true;
23779     },
23780     
23781     /**
23782      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23783      */
23784     afterRepair : function(){
23785         if(Roo.enableFx){
23786             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23787         }
23788         this.dragging = false;
23789     },
23790
23791     /**
23792      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23793      * the XY of this.dragData.ddel
23794      * @param {EventObject} e The mouse up event
23795      * @return {Array} The xy location (e.g. [100, 200])
23796      */
23797     getRepairXY : function(e){
23798         return Roo.Element.fly(this.dragData.ddel).getXY();  
23799     }
23800 });/*
23801  * Based on:
23802  * Ext JS Library 1.1.1
23803  * Copyright(c) 2006-2007, Ext JS, LLC.
23804  *
23805  * Originally Released Under LGPL - original licence link has changed is not relivant.
23806  *
23807  * Fork - LGPL
23808  * <script type="text/javascript">
23809  */
23810 /**
23811  * @class Roo.dd.DropZone
23812  * @extends Roo.dd.DropTarget
23813  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23814  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23815  * @constructor
23816  * @param {String/HTMLElement/Element} el The container element
23817  * @param {Object} config
23818  */
23819 Roo.dd.DropZone = function(el, config){
23820     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23821 };
23822
23823 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23824     /**
23825      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23826      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23827      * provide your own custom lookup.
23828      * @param {Event} e The event
23829      * @return {Object} data The custom data
23830      */
23831     getTargetFromEvent : function(e){
23832         return Roo.dd.Registry.getTargetFromEvent(e);
23833     },
23834
23835     /**
23836      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23837      * that it has registered.  This method has no default implementation and should be overridden to provide
23838      * node-specific processing if necessary.
23839      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23840      * {@link #getTargetFromEvent} for this node)
23841      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23842      * @param {Event} e The event
23843      * @param {Object} data An object containing arbitrary data supplied by the drag source
23844      */
23845     onNodeEnter : function(n, dd, e, data){
23846         
23847     },
23848
23849     /**
23850      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23851      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23852      * overridden to provide the proper feedback.
23853      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23854      * {@link #getTargetFromEvent} for this node)
23855      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23856      * @param {Event} e The event
23857      * @param {Object} data An object containing arbitrary data supplied by the drag source
23858      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23859      * underlying {@link Roo.dd.StatusProxy} can be updated
23860      */
23861     onNodeOver : function(n, dd, e, data){
23862         return this.dropAllowed;
23863     },
23864
23865     /**
23866      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23867      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23868      * node-specific processing if necessary.
23869      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23870      * {@link #getTargetFromEvent} for this node)
23871      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23872      * @param {Event} e The event
23873      * @param {Object} data An object containing arbitrary data supplied by the drag source
23874      */
23875     onNodeOut : function(n, dd, e, data){
23876         
23877     },
23878
23879     /**
23880      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23881      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23882      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23883      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23884      * {@link #getTargetFromEvent} for this node)
23885      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23886      * @param {Event} e The event
23887      * @param {Object} data An object containing arbitrary data supplied by the drag source
23888      * @return {Boolean} True if the drop was valid, else false
23889      */
23890     onNodeDrop : function(n, dd, e, data){
23891         return false;
23892     },
23893
23894     /**
23895      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23896      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23897      * it should be overridden to provide the proper feedback if necessary.
23898      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23899      * @param {Event} e The event
23900      * @param {Object} data An object containing arbitrary data supplied by the drag source
23901      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23902      * underlying {@link Roo.dd.StatusProxy} can be updated
23903      */
23904     onContainerOver : function(dd, e, data){
23905         return this.dropNotAllowed;
23906     },
23907
23908     /**
23909      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23910      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23911      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23912      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23913      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23914      * @param {Event} e The event
23915      * @param {Object} data An object containing arbitrary data supplied by the drag source
23916      * @return {Boolean} True if the drop was valid, else false
23917      */
23918     onContainerDrop : function(dd, e, data){
23919         return false;
23920     },
23921
23922     /**
23923      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23924      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23925      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23926      * you should override this method and provide a custom implementation.
23927      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23928      * @param {Event} e The event
23929      * @param {Object} data An object containing arbitrary data supplied by the drag source
23930      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23931      * underlying {@link Roo.dd.StatusProxy} can be updated
23932      */
23933     notifyEnter : function(dd, e, data){
23934         return this.dropNotAllowed;
23935     },
23936
23937     /**
23938      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23939      * This method will be called on every mouse movement while the drag source is over the drop zone.
23940      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23941      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23942      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23943      * registered node, it will call {@link #onContainerOver}.
23944      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23945      * @param {Event} e The event
23946      * @param {Object} data An object containing arbitrary data supplied by the drag source
23947      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23948      * underlying {@link Roo.dd.StatusProxy} can be updated
23949      */
23950     notifyOver : function(dd, e, data){
23951         var n = this.getTargetFromEvent(e);
23952         if(!n){ // not over valid drop target
23953             if(this.lastOverNode){
23954                 this.onNodeOut(this.lastOverNode, dd, e, data);
23955                 this.lastOverNode = null;
23956             }
23957             return this.onContainerOver(dd, e, data);
23958         }
23959         if(this.lastOverNode != n){
23960             if(this.lastOverNode){
23961                 this.onNodeOut(this.lastOverNode, dd, e, data);
23962             }
23963             this.onNodeEnter(n, dd, e, data);
23964             this.lastOverNode = n;
23965         }
23966         return this.onNodeOver(n, dd, e, data);
23967     },
23968
23969     /**
23970      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23971      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23972      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23973      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23974      * @param {Event} e The event
23975      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23976      */
23977     notifyOut : function(dd, e, data){
23978         if(this.lastOverNode){
23979             this.onNodeOut(this.lastOverNode, dd, e, data);
23980             this.lastOverNode = null;
23981         }
23982     },
23983
23984     /**
23985      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23986      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23987      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23988      * otherwise it will call {@link #onContainerDrop}.
23989      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23990      * @param {Event} e The event
23991      * @param {Object} data An object containing arbitrary data supplied by the drag source
23992      * @return {Boolean} True if the drop was valid, else false
23993      */
23994     notifyDrop : function(dd, e, data){
23995         if(this.lastOverNode){
23996             this.onNodeOut(this.lastOverNode, dd, e, data);
23997             this.lastOverNode = null;
23998         }
23999         var n = this.getTargetFromEvent(e);
24000         return n ?
24001             this.onNodeDrop(n, dd, e, data) :
24002             this.onContainerDrop(dd, e, data);
24003     },
24004
24005     // private
24006     triggerCacheRefresh : function(){
24007         Roo.dd.DDM.refreshCache(this.groups);
24008     }  
24009 });/*
24010  * Based on:
24011  * Ext JS Library 1.1.1
24012  * Copyright(c) 2006-2007, Ext JS, LLC.
24013  *
24014  * Originally Released Under LGPL - original licence link has changed is not relivant.
24015  *
24016  * Fork - LGPL
24017  * <script type="text/javascript">
24018  */
24019
24020
24021 /**
24022  * @class Roo.data.SortTypes
24023  * @static
24024  * Defines the default sorting (casting?) comparison functions used when sorting data.
24025  */
24026 Roo.data.SortTypes = {
24027     /**
24028      * Default sort that does nothing
24029      * @param {Mixed} s The value being converted
24030      * @return {Mixed} The comparison value
24031      */
24032     none : function(s){
24033         return s;
24034     },
24035     
24036     /**
24037      * The regular expression used to strip tags
24038      * @type {RegExp}
24039      * @property
24040      */
24041     stripTagsRE : /<\/?[^>]+>/gi,
24042     
24043     /**
24044      * Strips all HTML tags to sort on text only
24045      * @param {Mixed} s The value being converted
24046      * @return {String} The comparison value
24047      */
24048     asText : function(s){
24049         return String(s).replace(this.stripTagsRE, "");
24050     },
24051     
24052     /**
24053      * Strips all HTML tags to sort on text only - Case insensitive
24054      * @param {Mixed} s The value being converted
24055      * @return {String} The comparison value
24056      */
24057     asUCText : function(s){
24058         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24059     },
24060     
24061     /**
24062      * Case insensitive string
24063      * @param {Mixed} s The value being converted
24064      * @return {String} The comparison value
24065      */
24066     asUCString : function(s) {
24067         return String(s).toUpperCase();
24068     },
24069     
24070     /**
24071      * Date sorting
24072      * @param {Mixed} s The value being converted
24073      * @return {Number} The comparison value
24074      */
24075     asDate : function(s) {
24076         if(!s){
24077             return 0;
24078         }
24079         if(s instanceof Date){
24080             return s.getTime();
24081         }
24082         return Date.parse(String(s));
24083     },
24084     
24085     /**
24086      * Float sorting
24087      * @param {Mixed} s The value being converted
24088      * @return {Float} The comparison value
24089      */
24090     asFloat : function(s) {
24091         var val = parseFloat(String(s).replace(/,/g, ""));
24092         if(isNaN(val)) {
24093             val = 0;
24094         }
24095         return val;
24096     },
24097     
24098     /**
24099      * Integer sorting
24100      * @param {Mixed} s The value being converted
24101      * @return {Number} The comparison value
24102      */
24103     asInt : function(s) {
24104         var val = parseInt(String(s).replace(/,/g, ""));
24105         if(isNaN(val)) {
24106             val = 0;
24107         }
24108         return val;
24109     }
24110 };/*
24111  * Based on:
24112  * Ext JS Library 1.1.1
24113  * Copyright(c) 2006-2007, Ext JS, LLC.
24114  *
24115  * Originally Released Under LGPL - original licence link has changed is not relivant.
24116  *
24117  * Fork - LGPL
24118  * <script type="text/javascript">
24119  */
24120
24121 /**
24122 * @class Roo.data.Record
24123  * Instances of this class encapsulate both record <em>definition</em> information, and record
24124  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24125  * to access Records cached in an {@link Roo.data.Store} object.<br>
24126  * <p>
24127  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24128  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24129  * objects.<br>
24130  * <p>
24131  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24132  * @constructor
24133  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24134  * {@link #create}. The parameters are the same.
24135  * @param {Array} data An associative Array of data values keyed by the field name.
24136  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24137  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24138  * not specified an integer id is generated.
24139  */
24140 Roo.data.Record = function(data, id){
24141     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24142     this.data = data;
24143 };
24144
24145 /**
24146  * Generate a constructor for a specific record layout.
24147  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24148  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24149  * Each field definition object may contain the following properties: <ul>
24150  * <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,
24151  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24152  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24153  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24154  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24155  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24156  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24157  * this may be omitted.</p></li>
24158  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24159  * <ul><li>auto (Default, implies no conversion)</li>
24160  * <li>string</li>
24161  * <li>int</li>
24162  * <li>float</li>
24163  * <li>boolean</li>
24164  * <li>date</li></ul></p></li>
24165  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24166  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24167  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24168  * by the Reader into an object that will be stored in the Record. It is passed the
24169  * following parameters:<ul>
24170  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24171  * </ul></p></li>
24172  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24173  * </ul>
24174  * <br>usage:<br><pre><code>
24175 var TopicRecord = Roo.data.Record.create(
24176     {name: 'title', mapping: 'topic_title'},
24177     {name: 'author', mapping: 'username'},
24178     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24179     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24180     {name: 'lastPoster', mapping: 'user2'},
24181     {name: 'excerpt', mapping: 'post_text'}
24182 );
24183
24184 var myNewRecord = new TopicRecord({
24185     title: 'Do my job please',
24186     author: 'noobie',
24187     totalPosts: 1,
24188     lastPost: new Date(),
24189     lastPoster: 'Animal',
24190     excerpt: 'No way dude!'
24191 });
24192 myStore.add(myNewRecord);
24193 </code></pre>
24194  * @method create
24195  * @static
24196  */
24197 Roo.data.Record.create = function(o){
24198     var f = function(){
24199         f.superclass.constructor.apply(this, arguments);
24200     };
24201     Roo.extend(f, Roo.data.Record);
24202     var p = f.prototype;
24203     p.fields = new Roo.util.MixedCollection(false, function(field){
24204         return field.name;
24205     });
24206     for(var i = 0, len = o.length; i < len; i++){
24207         p.fields.add(new Roo.data.Field(o[i]));
24208     }
24209     f.getField = function(name){
24210         return p.fields.get(name);  
24211     };
24212     return f;
24213 };
24214
24215 Roo.data.Record.AUTO_ID = 1000;
24216 Roo.data.Record.EDIT = 'edit';
24217 Roo.data.Record.REJECT = 'reject';
24218 Roo.data.Record.COMMIT = 'commit';
24219
24220 Roo.data.Record.prototype = {
24221     /**
24222      * Readonly flag - true if this record has been modified.
24223      * @type Boolean
24224      */
24225     dirty : false,
24226     editing : false,
24227     error: null,
24228     modified: null,
24229
24230     // private
24231     join : function(store){
24232         this.store = store;
24233     },
24234
24235     /**
24236      * Set the named field to the specified value.
24237      * @param {String} name The name of the field to set.
24238      * @param {Object} value The value to set the field to.
24239      */
24240     set : function(name, value){
24241         if(this.data[name] == value){
24242             return;
24243         }
24244         this.dirty = true;
24245         if(!this.modified){
24246             this.modified = {};
24247         }
24248         if(typeof this.modified[name] == 'undefined'){
24249             this.modified[name] = this.data[name];
24250         }
24251         this.data[name] = value;
24252         if(!this.editing && this.store){
24253             this.store.afterEdit(this);
24254         }       
24255     },
24256
24257     /**
24258      * Get the value of the named field.
24259      * @param {String} name The name of the field to get the value of.
24260      * @return {Object} The value of the field.
24261      */
24262     get : function(name){
24263         return this.data[name]; 
24264     },
24265
24266     // private
24267     beginEdit : function(){
24268         this.editing = true;
24269         this.modified = {}; 
24270     },
24271
24272     // private
24273     cancelEdit : function(){
24274         this.editing = false;
24275         delete this.modified;
24276     },
24277
24278     // private
24279     endEdit : function(){
24280         this.editing = false;
24281         if(this.dirty && this.store){
24282             this.store.afterEdit(this);
24283         }
24284     },
24285
24286     /**
24287      * Usually called by the {@link Roo.data.Store} which owns the Record.
24288      * Rejects all changes made to the Record since either creation, or the last commit operation.
24289      * Modified fields are reverted to their original values.
24290      * <p>
24291      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24292      * of reject operations.
24293      */
24294     reject : function(){
24295         var m = this.modified;
24296         for(var n in m){
24297             if(typeof m[n] != "function"){
24298                 this.data[n] = m[n];
24299             }
24300         }
24301         this.dirty = false;
24302         delete this.modified;
24303         this.editing = false;
24304         if(this.store){
24305             this.store.afterReject(this);
24306         }
24307     },
24308
24309     /**
24310      * Usually called by the {@link Roo.data.Store} which owns the Record.
24311      * Commits all changes made to the Record since either creation, or the last commit operation.
24312      * <p>
24313      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24314      * of commit operations.
24315      */
24316     commit : function(){
24317         this.dirty = false;
24318         delete this.modified;
24319         this.editing = false;
24320         if(this.store){
24321             this.store.afterCommit(this);
24322         }
24323     },
24324
24325     // private
24326     hasError : function(){
24327         return this.error != null;
24328     },
24329
24330     // private
24331     clearError : function(){
24332         this.error = null;
24333     },
24334
24335     /**
24336      * Creates a copy of this record.
24337      * @param {String} id (optional) A new record id if you don't want to use this record's id
24338      * @return {Record}
24339      */
24340     copy : function(newId) {
24341         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24342     }
24343 };/*
24344  * Based on:
24345  * Ext JS Library 1.1.1
24346  * Copyright(c) 2006-2007, Ext JS, LLC.
24347  *
24348  * Originally Released Under LGPL - original licence link has changed is not relivant.
24349  *
24350  * Fork - LGPL
24351  * <script type="text/javascript">
24352  */
24353
24354
24355
24356 /**
24357  * @class Roo.data.Store
24358  * @extends Roo.util.Observable
24359  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24360  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24361  * <p>
24362  * 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
24363  * has no knowledge of the format of the data returned by the Proxy.<br>
24364  * <p>
24365  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24366  * instances from the data object. These records are cached and made available through accessor functions.
24367  * @constructor
24368  * Creates a new Store.
24369  * @param {Object} config A config object containing the objects needed for the Store to access data,
24370  * and read the data into Records.
24371  */
24372 Roo.data.Store = function(config){
24373     this.data = new Roo.util.MixedCollection(false);
24374     this.data.getKey = function(o){
24375         return o.id;
24376     };
24377     this.baseParams = {};
24378     // private
24379     this.paramNames = {
24380         "start" : "start",
24381         "limit" : "limit",
24382         "sort" : "sort",
24383         "dir" : "dir",
24384         "multisort" : "_multisort"
24385     };
24386
24387     if(config && config.data){
24388         this.inlineData = config.data;
24389         delete config.data;
24390     }
24391
24392     Roo.apply(this, config);
24393     
24394     if(this.reader){ // reader passed
24395         this.reader = Roo.factory(this.reader, Roo.data);
24396         this.reader.xmodule = this.xmodule || false;
24397         if(!this.recordType){
24398             this.recordType = this.reader.recordType;
24399         }
24400         if(this.reader.onMetaChange){
24401             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24402         }
24403     }
24404
24405     if(this.recordType){
24406         this.fields = this.recordType.prototype.fields;
24407     }
24408     this.modified = [];
24409
24410     this.addEvents({
24411         /**
24412          * @event datachanged
24413          * Fires when the data cache has changed, and a widget which is using this Store
24414          * as a Record cache should refresh its view.
24415          * @param {Store} this
24416          */
24417         datachanged : true,
24418         /**
24419          * @event metachange
24420          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24421          * @param {Store} this
24422          * @param {Object} meta The JSON metadata
24423          */
24424         metachange : true,
24425         /**
24426          * @event add
24427          * Fires when Records have been added to the Store
24428          * @param {Store} this
24429          * @param {Roo.data.Record[]} records The array of Records added
24430          * @param {Number} index The index at which the record(s) were added
24431          */
24432         add : true,
24433         /**
24434          * @event remove
24435          * Fires when a Record has been removed from the Store
24436          * @param {Store} this
24437          * @param {Roo.data.Record} record The Record that was removed
24438          * @param {Number} index The index at which the record was removed
24439          */
24440         remove : true,
24441         /**
24442          * @event update
24443          * Fires when a Record has been updated
24444          * @param {Store} this
24445          * @param {Roo.data.Record} record The Record that was updated
24446          * @param {String} operation The update operation being performed.  Value may be one of:
24447          * <pre><code>
24448  Roo.data.Record.EDIT
24449  Roo.data.Record.REJECT
24450  Roo.data.Record.COMMIT
24451          * </code></pre>
24452          */
24453         update : true,
24454         /**
24455          * @event clear
24456          * Fires when the data cache has been cleared.
24457          * @param {Store} this
24458          */
24459         clear : true,
24460         /**
24461          * @event beforeload
24462          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24463          * the load action will be canceled.
24464          * @param {Store} this
24465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24466          */
24467         beforeload : true,
24468         /**
24469          * @event beforeloadadd
24470          * Fires after a new set of Records has been loaded.
24471          * @param {Store} this
24472          * @param {Roo.data.Record[]} records The Records that were loaded
24473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24474          */
24475         beforeloadadd : true,
24476         /**
24477          * @event load
24478          * Fires after a new set of Records has been loaded, before they are added to the store.
24479          * @param {Store} this
24480          * @param {Roo.data.Record[]} records The Records that were loaded
24481          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24482          * @params {Object} return from reader
24483          */
24484         load : true,
24485         /**
24486          * @event loadexception
24487          * Fires if an exception occurs in the Proxy during loading.
24488          * Called with the signature of the Proxy's "loadexception" event.
24489          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24490          * 
24491          * @param {Proxy} 
24492          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24493          * @param {Object} load options 
24494          * @param {Object} jsonData from your request (normally this contains the Exception)
24495          */
24496         loadexception : true
24497     });
24498     
24499     if(this.proxy){
24500         this.proxy = Roo.factory(this.proxy, Roo.data);
24501         this.proxy.xmodule = this.xmodule || false;
24502         this.relayEvents(this.proxy,  ["loadexception"]);
24503     }
24504     this.sortToggle = {};
24505     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24506
24507     Roo.data.Store.superclass.constructor.call(this);
24508
24509     if(this.inlineData){
24510         this.loadData(this.inlineData);
24511         delete this.inlineData;
24512     }
24513 };
24514
24515 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24516      /**
24517     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24518     * without a remote query - used by combo/forms at present.
24519     */
24520     
24521     /**
24522     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24523     */
24524     /**
24525     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24526     */
24527     /**
24528     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24529     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24530     */
24531     /**
24532     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24533     * on any HTTP request
24534     */
24535     /**
24536     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24537     */
24538     /**
24539     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24540     */
24541     multiSort: false,
24542     /**
24543     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24544     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24545     */
24546     remoteSort : false,
24547
24548     /**
24549     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24550      * loaded or when a record is removed. (defaults to false).
24551     */
24552     pruneModifiedRecords : false,
24553
24554     // private
24555     lastOptions : null,
24556
24557     /**
24558      * Add Records to the Store and fires the add event.
24559      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24560      */
24561     add : function(records){
24562         records = [].concat(records);
24563         for(var i = 0, len = records.length; i < len; i++){
24564             records[i].join(this);
24565         }
24566         var index = this.data.length;
24567         this.data.addAll(records);
24568         this.fireEvent("add", this, records, index);
24569     },
24570
24571     /**
24572      * Remove a Record from the Store and fires the remove event.
24573      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24574      */
24575     remove : function(record){
24576         var index = this.data.indexOf(record);
24577         this.data.removeAt(index);
24578  
24579         if(this.pruneModifiedRecords){
24580             this.modified.remove(record);
24581         }
24582         this.fireEvent("remove", this, record, index);
24583     },
24584
24585     /**
24586      * Remove all Records from the Store and fires the clear event.
24587      */
24588     removeAll : function(){
24589         this.data.clear();
24590         if(this.pruneModifiedRecords){
24591             this.modified = [];
24592         }
24593         this.fireEvent("clear", this);
24594     },
24595
24596     /**
24597      * Inserts Records to the Store at the given index and fires the add event.
24598      * @param {Number} index The start index at which to insert the passed Records.
24599      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24600      */
24601     insert : function(index, records){
24602         records = [].concat(records);
24603         for(var i = 0, len = records.length; i < len; i++){
24604             this.data.insert(index, records[i]);
24605             records[i].join(this);
24606         }
24607         this.fireEvent("add", this, records, index);
24608     },
24609
24610     /**
24611      * Get the index within the cache of the passed Record.
24612      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24613      * @return {Number} The index of the passed Record. Returns -1 if not found.
24614      */
24615     indexOf : function(record){
24616         return this.data.indexOf(record);
24617     },
24618
24619     /**
24620      * Get the index within the cache of the Record with the passed id.
24621      * @param {String} id The id of the Record to find.
24622      * @return {Number} The index of the Record. Returns -1 if not found.
24623      */
24624     indexOfId : function(id){
24625         return this.data.indexOfKey(id);
24626     },
24627
24628     /**
24629      * Get the Record with the specified id.
24630      * @param {String} id The id of the Record to find.
24631      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24632      */
24633     getById : function(id){
24634         return this.data.key(id);
24635     },
24636
24637     /**
24638      * Get the Record at the specified index.
24639      * @param {Number} index The index of the Record to find.
24640      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24641      */
24642     getAt : function(index){
24643         return this.data.itemAt(index);
24644     },
24645
24646     /**
24647      * Returns a range of Records between specified indices.
24648      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24649      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24650      * @return {Roo.data.Record[]} An array of Records
24651      */
24652     getRange : function(start, end){
24653         return this.data.getRange(start, end);
24654     },
24655
24656     // private
24657     storeOptions : function(o){
24658         o = Roo.apply({}, o);
24659         delete o.callback;
24660         delete o.scope;
24661         this.lastOptions = o;
24662     },
24663
24664     /**
24665      * Loads the Record cache from the configured Proxy using the configured Reader.
24666      * <p>
24667      * If using remote paging, then the first load call must specify the <em>start</em>
24668      * and <em>limit</em> properties in the options.params property to establish the initial
24669      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24670      * <p>
24671      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24672      * and this call will return before the new data has been loaded. Perform any post-processing
24673      * in a callback function, or in a "load" event handler.</strong>
24674      * <p>
24675      * @param {Object} options An object containing properties which control loading options:<ul>
24676      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24677      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24678      * <pre>
24679                 {
24680                     data : data,  // array of key=>value data like JsonReader
24681                     total : data.length,
24682                     success : true
24683                     
24684                 }
24685         </pre>
24686             }.</li>
24687      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24688      * passed the following arguments:<ul>
24689      * <li>r : Roo.data.Record[]</li>
24690      * <li>options: Options object from the load call</li>
24691      * <li>success: Boolean success indicator</li></ul></li>
24692      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24693      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24694      * </ul>
24695      */
24696     load : function(options){
24697         options = options || {};
24698         if(this.fireEvent("beforeload", this, options) !== false){
24699             this.storeOptions(options);
24700             var p = Roo.apply(options.params || {}, this.baseParams);
24701             // if meta was not loaded from remote source.. try requesting it.
24702             if (!this.reader.metaFromRemote) {
24703                 p._requestMeta = 1;
24704             }
24705             if(this.sortInfo && this.remoteSort){
24706                 var pn = this.paramNames;
24707                 p[pn["sort"]] = this.sortInfo.field;
24708                 p[pn["dir"]] = this.sortInfo.direction;
24709             }
24710             if (this.multiSort) {
24711                 var pn = this.paramNames;
24712                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24713             }
24714             
24715             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24716         }
24717     },
24718
24719     /**
24720      * Reloads the Record cache from the configured Proxy using the configured Reader and
24721      * the options from the last load operation performed.
24722      * @param {Object} options (optional) An object containing properties which may override the options
24723      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24724      * the most recently used options are reused).
24725      */
24726     reload : function(options){
24727         this.load(Roo.applyIf(options||{}, this.lastOptions));
24728     },
24729
24730     // private
24731     // Called as a callback by the Reader during a load operation.
24732     loadRecords : function(o, options, success){
24733          
24734         if(!o){
24735             if(success !== false){
24736                 this.fireEvent("load", this, [], options, o);
24737             }
24738             if(options.callback){
24739                 options.callback.call(options.scope || this, [], options, false);
24740             }
24741             return;
24742         }
24743         // if data returned failure - throw an exception.
24744         if (o.success === false) {
24745             // show a message if no listener is registered.
24746             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24747                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24748             }
24749             // loadmask wil be hooked into this..
24750             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24751             return;
24752         }
24753         var r = o.records, t = o.totalRecords || r.length;
24754         
24755         this.fireEvent("beforeloadadd", this, r, options, o);
24756         
24757         if(!options || options.add !== true){
24758             if(this.pruneModifiedRecords){
24759                 this.modified = [];
24760             }
24761             for(var i = 0, len = r.length; i < len; i++){
24762                 r[i].join(this);
24763             }
24764             if(this.snapshot){
24765                 this.data = this.snapshot;
24766                 delete this.snapshot;
24767             }
24768             this.data.clear();
24769             this.data.addAll(r);
24770             this.totalLength = t;
24771             this.applySort();
24772             this.fireEvent("datachanged", this);
24773         }else{
24774             this.totalLength = Math.max(t, this.data.length+r.length);
24775             this.add(r);
24776         }
24777         
24778         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24779                 
24780             var e = new Roo.data.Record({});
24781
24782             e.set(this.parent.displayField, this.parent.emptyTitle);
24783             e.set(this.parent.valueField, '');
24784
24785             this.insert(0, e);
24786         }
24787             
24788         this.fireEvent("load", this, r, options, o);
24789         if(options.callback){
24790             options.callback.call(options.scope || this, r, options, true);
24791         }
24792     },
24793
24794
24795     /**
24796      * Loads data from a passed data block. A Reader which understands the format of the data
24797      * must have been configured in the constructor.
24798      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24799      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24800      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24801      */
24802     loadData : function(o, append){
24803         var r = this.reader.readRecords(o);
24804         this.loadRecords(r, {add: append}, true);
24805     },
24806     
24807      /**
24808      * using 'cn' the nested child reader read the child array into it's child stores.
24809      * @param {Object} rec The record with a 'children array
24810      */
24811     loadDataFromChildren : function(rec)
24812     {
24813         this.loadData(this.reader.toLoadData(rec));
24814     },
24815     
24816
24817     /**
24818      * Gets the number of cached records.
24819      * <p>
24820      * <em>If using paging, this may not be the total size of the dataset. If the data object
24821      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24822      * the data set size</em>
24823      */
24824     getCount : function(){
24825         return this.data.length || 0;
24826     },
24827
24828     /**
24829      * Gets the total number of records in the dataset as returned by the server.
24830      * <p>
24831      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24832      * the dataset size</em>
24833      */
24834     getTotalCount : function(){
24835         return this.totalLength || 0;
24836     },
24837
24838     /**
24839      * Returns the sort state of the Store as an object with two properties:
24840      * <pre><code>
24841  field {String} The name of the field by which the Records are sorted
24842  direction {String} The sort order, "ASC" or "DESC"
24843      * </code></pre>
24844      */
24845     getSortState : function(){
24846         return this.sortInfo;
24847     },
24848
24849     // private
24850     applySort : function(){
24851         if(this.sortInfo && !this.remoteSort){
24852             var s = this.sortInfo, f = s.field;
24853             var st = this.fields.get(f).sortType;
24854             var fn = function(r1, r2){
24855                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24856                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24857             };
24858             this.data.sort(s.direction, fn);
24859             if(this.snapshot && this.snapshot != this.data){
24860                 this.snapshot.sort(s.direction, fn);
24861             }
24862         }
24863     },
24864
24865     /**
24866      * Sets the default sort column and order to be used by the next load operation.
24867      * @param {String} fieldName The name of the field to sort by.
24868      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24869      */
24870     setDefaultSort : function(field, dir){
24871         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24872     },
24873
24874     /**
24875      * Sort the Records.
24876      * If remote sorting is used, the sort is performed on the server, and the cache is
24877      * reloaded. If local sorting is used, the cache is sorted internally.
24878      * @param {String} fieldName The name of the field to sort by.
24879      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24880      */
24881     sort : function(fieldName, dir){
24882         var f = this.fields.get(fieldName);
24883         if(!dir){
24884             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24885             
24886             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24887                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24888             }else{
24889                 dir = f.sortDir;
24890             }
24891         }
24892         this.sortToggle[f.name] = dir;
24893         this.sortInfo = {field: f.name, direction: dir};
24894         if(!this.remoteSort){
24895             this.applySort();
24896             this.fireEvent("datachanged", this);
24897         }else{
24898             this.load(this.lastOptions);
24899         }
24900     },
24901
24902     /**
24903      * Calls the specified function for each of the Records in the cache.
24904      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24905      * Returning <em>false</em> aborts and exits the iteration.
24906      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24907      */
24908     each : function(fn, scope){
24909         this.data.each(fn, scope);
24910     },
24911
24912     /**
24913      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24914      * (e.g., during paging).
24915      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24916      */
24917     getModifiedRecords : function(){
24918         return this.modified;
24919     },
24920
24921     // private
24922     createFilterFn : function(property, value, anyMatch){
24923         if(!value.exec){ // not a regex
24924             value = String(value);
24925             if(value.length == 0){
24926                 return false;
24927             }
24928             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24929         }
24930         return function(r){
24931             return value.test(r.data[property]);
24932         };
24933     },
24934
24935     /**
24936      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24937      * @param {String} property A field on your records
24938      * @param {Number} start The record index to start at (defaults to 0)
24939      * @param {Number} end The last record index to include (defaults to length - 1)
24940      * @return {Number} The sum
24941      */
24942     sum : function(property, start, end){
24943         var rs = this.data.items, v = 0;
24944         start = start || 0;
24945         end = (end || end === 0) ? end : rs.length-1;
24946
24947         for(var i = start; i <= end; i++){
24948             v += (rs[i].data[property] || 0);
24949         }
24950         return v;
24951     },
24952
24953     /**
24954      * Filter the records by a specified property.
24955      * @param {String} field A field on your records
24956      * @param {String/RegExp} value Either a string that the field
24957      * should start with or a RegExp to test against the field
24958      * @param {Boolean} anyMatch True to match any part not just the beginning
24959      */
24960     filter : function(property, value, anyMatch){
24961         var fn = this.createFilterFn(property, value, anyMatch);
24962         return fn ? this.filterBy(fn) : this.clearFilter();
24963     },
24964
24965     /**
24966      * Filter by a function. The specified function will be called with each
24967      * record in this data source. If the function returns true the record is included,
24968      * otherwise it is filtered.
24969      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24970      * @param {Object} scope (optional) The scope of the function (defaults to this)
24971      */
24972     filterBy : function(fn, scope){
24973         this.snapshot = this.snapshot || this.data;
24974         this.data = this.queryBy(fn, scope||this);
24975         this.fireEvent("datachanged", this);
24976     },
24977
24978     /**
24979      * Query the records by a specified property.
24980      * @param {String} field A field on your records
24981      * @param {String/RegExp} value Either a string that the field
24982      * should start with or a RegExp to test against the field
24983      * @param {Boolean} anyMatch True to match any part not just the beginning
24984      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24985      */
24986     query : function(property, value, anyMatch){
24987         var fn = this.createFilterFn(property, value, anyMatch);
24988         return fn ? this.queryBy(fn) : this.data.clone();
24989     },
24990
24991     /**
24992      * Query by a function. The specified function will be called with each
24993      * record in this data source. If the function returns true the record is included
24994      * in the results.
24995      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24996      * @param {Object} scope (optional) The scope of the function (defaults to this)
24997       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24998      **/
24999     queryBy : function(fn, scope){
25000         var data = this.snapshot || this.data;
25001         return data.filterBy(fn, scope||this);
25002     },
25003
25004     /**
25005      * Collects unique values for a particular dataIndex from this store.
25006      * @param {String} dataIndex The property to collect
25007      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25008      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25009      * @return {Array} An array of the unique values
25010      **/
25011     collect : function(dataIndex, allowNull, bypassFilter){
25012         var d = (bypassFilter === true && this.snapshot) ?
25013                 this.snapshot.items : this.data.items;
25014         var v, sv, r = [], l = {};
25015         for(var i = 0, len = d.length; i < len; i++){
25016             v = d[i].data[dataIndex];
25017             sv = String(v);
25018             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25019                 l[sv] = true;
25020                 r[r.length] = v;
25021             }
25022         }
25023         return r;
25024     },
25025
25026     /**
25027      * Revert to a view of the Record cache with no filtering applied.
25028      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25029      */
25030     clearFilter : function(suppressEvent){
25031         if(this.snapshot && this.snapshot != this.data){
25032             this.data = this.snapshot;
25033             delete this.snapshot;
25034             if(suppressEvent !== true){
25035                 this.fireEvent("datachanged", this);
25036             }
25037         }
25038     },
25039
25040     // private
25041     afterEdit : function(record){
25042         if(this.modified.indexOf(record) == -1){
25043             this.modified.push(record);
25044         }
25045         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25046     },
25047     
25048     // private
25049     afterReject : function(record){
25050         this.modified.remove(record);
25051         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25052     },
25053
25054     // private
25055     afterCommit : function(record){
25056         this.modified.remove(record);
25057         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25058     },
25059
25060     /**
25061      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25062      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25063      */
25064     commitChanges : function(){
25065         var m = this.modified.slice(0);
25066         this.modified = [];
25067         for(var i = 0, len = m.length; i < len; i++){
25068             m[i].commit();
25069         }
25070     },
25071
25072     /**
25073      * Cancel outstanding changes on all changed records.
25074      */
25075     rejectChanges : function(){
25076         var m = this.modified.slice(0);
25077         this.modified = [];
25078         for(var i = 0, len = m.length; i < len; i++){
25079             m[i].reject();
25080         }
25081     },
25082
25083     onMetaChange : function(meta, rtype, o){
25084         this.recordType = rtype;
25085         this.fields = rtype.prototype.fields;
25086         delete this.snapshot;
25087         this.sortInfo = meta.sortInfo || this.sortInfo;
25088         this.modified = [];
25089         this.fireEvent('metachange', this, this.reader.meta);
25090     },
25091     
25092     moveIndex : function(data, type)
25093     {
25094         var index = this.indexOf(data);
25095         
25096         var newIndex = index + type;
25097         
25098         this.remove(data);
25099         
25100         this.insert(newIndex, data);
25101         
25102     }
25103 });/*
25104  * Based on:
25105  * Ext JS Library 1.1.1
25106  * Copyright(c) 2006-2007, Ext JS, LLC.
25107  *
25108  * Originally Released Under LGPL - original licence link has changed is not relivant.
25109  *
25110  * Fork - LGPL
25111  * <script type="text/javascript">
25112  */
25113
25114 /**
25115  * @class Roo.data.SimpleStore
25116  * @extends Roo.data.Store
25117  * Small helper class to make creating Stores from Array data easier.
25118  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25119  * @cfg {Array} fields An array of field definition objects, or field name strings.
25120  * @cfg {Object} an existing reader (eg. copied from another store)
25121  * @cfg {Array} data The multi-dimensional array of data
25122  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25123  * @cfg {Roo.data.Reader} reader  [not-required] 
25124  * @constructor
25125  * @param {Object} config
25126  */
25127 Roo.data.SimpleStore = function(config)
25128 {
25129     Roo.data.SimpleStore.superclass.constructor.call(this, {
25130         isLocal : true,
25131         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25132                 id: config.id
25133             },
25134             Roo.data.Record.create(config.fields)
25135         ),
25136         proxy : new Roo.data.MemoryProxy(config.data)
25137     });
25138     this.load();
25139 };
25140 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25141  * Based on:
25142  * Ext JS Library 1.1.1
25143  * Copyright(c) 2006-2007, Ext JS, LLC.
25144  *
25145  * Originally Released Under LGPL - original licence link has changed is not relivant.
25146  *
25147  * Fork - LGPL
25148  * <script type="text/javascript">
25149  */
25150
25151 /**
25152 /**
25153  * @extends Roo.data.Store
25154  * @class Roo.data.JsonStore
25155  * Small helper class to make creating Stores for JSON data easier. <br/>
25156 <pre><code>
25157 var store = new Roo.data.JsonStore({
25158     url: 'get-images.php',
25159     root: 'images',
25160     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25161 });
25162 </code></pre>
25163  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25164  * JsonReader and HttpProxy (unless inline data is provided).</b>
25165  * @cfg {Array} fields An array of field definition objects, or field name strings.
25166  * @constructor
25167  * @param {Object} config
25168  */
25169 Roo.data.JsonStore = function(c){
25170     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25171         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25172         reader: new Roo.data.JsonReader(c, c.fields)
25173     }));
25174 };
25175 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25176  * Based on:
25177  * Ext JS Library 1.1.1
25178  * Copyright(c) 2006-2007, Ext JS, LLC.
25179  *
25180  * Originally Released Under LGPL - original licence link has changed is not relivant.
25181  *
25182  * Fork - LGPL
25183  * <script type="text/javascript">
25184  */
25185
25186  
25187 Roo.data.Field = function(config){
25188     if(typeof config == "string"){
25189         config = {name: config};
25190     }
25191     Roo.apply(this, config);
25192     
25193     if(!this.type){
25194         this.type = "auto";
25195     }
25196     
25197     var st = Roo.data.SortTypes;
25198     // named sortTypes are supported, here we look them up
25199     if(typeof this.sortType == "string"){
25200         this.sortType = st[this.sortType];
25201     }
25202     
25203     // set default sortType for strings and dates
25204     if(!this.sortType){
25205         switch(this.type){
25206             case "string":
25207                 this.sortType = st.asUCString;
25208                 break;
25209             case "date":
25210                 this.sortType = st.asDate;
25211                 break;
25212             default:
25213                 this.sortType = st.none;
25214         }
25215     }
25216
25217     // define once
25218     var stripRe = /[\$,%]/g;
25219
25220     // prebuilt conversion function for this field, instead of
25221     // switching every time we're reading a value
25222     if(!this.convert){
25223         var cv, dateFormat = this.dateFormat;
25224         switch(this.type){
25225             case "":
25226             case "auto":
25227             case undefined:
25228                 cv = function(v){ return v; };
25229                 break;
25230             case "string":
25231                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25232                 break;
25233             case "int":
25234                 cv = function(v){
25235                     return v !== undefined && v !== null && v !== '' ?
25236                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25237                     };
25238                 break;
25239             case "float":
25240                 cv = function(v){
25241                     return v !== undefined && v !== null && v !== '' ?
25242                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25243                     };
25244                 break;
25245             case "bool":
25246             case "boolean":
25247                 cv = function(v){ return v === true || v === "true" || v == 1; };
25248                 break;
25249             case "date":
25250                 cv = function(v){
25251                     if(!v){
25252                         return '';
25253                     }
25254                     if(v instanceof Date){
25255                         return v;
25256                     }
25257                     if(dateFormat){
25258                         if(dateFormat == "timestamp"){
25259                             return new Date(v*1000);
25260                         }
25261                         return Date.parseDate(v, dateFormat);
25262                     }
25263                     var parsed = Date.parse(v);
25264                     return parsed ? new Date(parsed) : null;
25265                 };
25266              break;
25267             
25268         }
25269         this.convert = cv;
25270     }
25271 };
25272
25273 Roo.data.Field.prototype = {
25274     dateFormat: null,
25275     defaultValue: "",
25276     mapping: null,
25277     sortType : null,
25278     sortDir : "ASC"
25279 };/*
25280  * Based on:
25281  * Ext JS Library 1.1.1
25282  * Copyright(c) 2006-2007, Ext JS, LLC.
25283  *
25284  * Originally Released Under LGPL - original licence link has changed is not relivant.
25285  *
25286  * Fork - LGPL
25287  * <script type="text/javascript">
25288  */
25289  
25290 // Base class for reading structured data from a data source.  This class is intended to be
25291 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25292
25293 /**
25294  * @class Roo.data.DataReader
25295  * @abstract
25296  * Base class for reading structured data from a data source.  This class is intended to be
25297  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25298  */
25299
25300 Roo.data.DataReader = function(meta, recordType){
25301     
25302     this.meta = meta;
25303     
25304     this.recordType = recordType instanceof Array ? 
25305         Roo.data.Record.create(recordType) : recordType;
25306 };
25307
25308 Roo.data.DataReader.prototype = {
25309     
25310     
25311     readerType : 'Data',
25312      /**
25313      * Create an empty record
25314      * @param {Object} data (optional) - overlay some values
25315      * @return {Roo.data.Record} record created.
25316      */
25317     newRow :  function(d) {
25318         var da =  {};
25319         this.recordType.prototype.fields.each(function(c) {
25320             switch( c.type) {
25321                 case 'int' : da[c.name] = 0; break;
25322                 case 'date' : da[c.name] = new Date(); break;
25323                 case 'float' : da[c.name] = 0.0; break;
25324                 case 'boolean' : da[c.name] = false; break;
25325                 default : da[c.name] = ""; break;
25326             }
25327             
25328         });
25329         return new this.recordType(Roo.apply(da, d));
25330     }
25331     
25332     
25333 };/*
25334  * Based on:
25335  * Ext JS Library 1.1.1
25336  * Copyright(c) 2006-2007, Ext JS, LLC.
25337  *
25338  * Originally Released Under LGPL - original licence link has changed is not relivant.
25339  *
25340  * Fork - LGPL
25341  * <script type="text/javascript">
25342  */
25343
25344 /**
25345  * @class Roo.data.DataProxy
25346  * @extends Roo.util.Observable
25347  * @abstract
25348  * This class is an abstract base class for implementations which provide retrieval of
25349  * unformatted data objects.<br>
25350  * <p>
25351  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25352  * (of the appropriate type which knows how to parse the data object) to provide a block of
25353  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25354  * <p>
25355  * Custom implementations must implement the load method as described in
25356  * {@link Roo.data.HttpProxy#load}.
25357  */
25358 Roo.data.DataProxy = function(){
25359     this.addEvents({
25360         /**
25361          * @event beforeload
25362          * Fires before a network request is made to retrieve a data object.
25363          * @param {Object} This DataProxy object.
25364          * @param {Object} params The params parameter to the load function.
25365          */
25366         beforeload : true,
25367         /**
25368          * @event load
25369          * Fires before the load method's callback is called.
25370          * @param {Object} This DataProxy object.
25371          * @param {Object} o The data object.
25372          * @param {Object} arg The callback argument object passed to the load function.
25373          */
25374         load : true,
25375         /**
25376          * @event loadexception
25377          * Fires if an Exception occurs during data retrieval.
25378          * @param {Object} This DataProxy object.
25379          * @param {Object} o The data object.
25380          * @param {Object} arg The callback argument object passed to the load function.
25381          * @param {Object} e The Exception.
25382          */
25383         loadexception : true
25384     });
25385     Roo.data.DataProxy.superclass.constructor.call(this);
25386 };
25387
25388 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25389
25390     /**
25391      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25392      */
25393 /*
25394  * Based on:
25395  * Ext JS Library 1.1.1
25396  * Copyright(c) 2006-2007, Ext JS, LLC.
25397  *
25398  * Originally Released Under LGPL - original licence link has changed is not relivant.
25399  *
25400  * Fork - LGPL
25401  * <script type="text/javascript">
25402  */
25403 /**
25404  * @class Roo.data.MemoryProxy
25405  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25406  * to the Reader when its load method is called.
25407  * @constructor
25408  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25409  */
25410 Roo.data.MemoryProxy = function(data){
25411     if (data.data) {
25412         data = data.data;
25413     }
25414     Roo.data.MemoryProxy.superclass.constructor.call(this);
25415     this.data = data;
25416 };
25417
25418 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25419     
25420     /**
25421      * Load data from the requested source (in this case an in-memory
25422      * data object passed to the constructor), read the data object into
25423      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25424      * process that block using the passed callback.
25425      * @param {Object} params This parameter is not used by the MemoryProxy class.
25426      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25427      * object into a block of Roo.data.Records.
25428      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25429      * The function must be passed <ul>
25430      * <li>The Record block object</li>
25431      * <li>The "arg" argument from the load function</li>
25432      * <li>A boolean success indicator</li>
25433      * </ul>
25434      * @param {Object} scope The scope in which to call the callback
25435      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25436      */
25437     load : function(params, reader, callback, scope, arg){
25438         params = params || {};
25439         var result;
25440         try {
25441             result = reader.readRecords(params.data ? params.data :this.data);
25442         }catch(e){
25443             this.fireEvent("loadexception", this, arg, null, e);
25444             callback.call(scope, null, arg, false);
25445             return;
25446         }
25447         callback.call(scope, result, arg, true);
25448     },
25449     
25450     // private
25451     update : function(params, records){
25452         
25453     }
25454 });/*
25455  * Based on:
25456  * Ext JS Library 1.1.1
25457  * Copyright(c) 2006-2007, Ext JS, LLC.
25458  *
25459  * Originally Released Under LGPL - original licence link has changed is not relivant.
25460  *
25461  * Fork - LGPL
25462  * <script type="text/javascript">
25463  */
25464 /**
25465  * @class Roo.data.HttpProxy
25466  * @extends Roo.data.DataProxy
25467  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25468  * configured to reference a certain URL.<br><br>
25469  * <p>
25470  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25471  * from which the running page was served.<br><br>
25472  * <p>
25473  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25474  * <p>
25475  * Be aware that to enable the browser to parse an XML document, the server must set
25476  * the Content-Type header in the HTTP response to "text/xml".
25477  * @constructor
25478  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25479  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25480  * will be used to make the request.
25481  */
25482 Roo.data.HttpProxy = function(conn){
25483     Roo.data.HttpProxy.superclass.constructor.call(this);
25484     // is conn a conn config or a real conn?
25485     this.conn = conn;
25486     this.useAjax = !conn || !conn.events;
25487   
25488 };
25489
25490 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25491     // thse are take from connection...
25492     
25493     /**
25494      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25495      */
25496     /**
25497      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25498      * extra parameters to each request made by this object. (defaults to undefined)
25499      */
25500     /**
25501      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25502      *  to each request made by this object. (defaults to undefined)
25503      */
25504     /**
25505      * @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)
25506      */
25507     /**
25508      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25509      */
25510      /**
25511      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25512      * @type Boolean
25513      */
25514   
25515
25516     /**
25517      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25518      * @type Boolean
25519      */
25520     /**
25521      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25522      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25523      * a finer-grained basis than the DataProxy events.
25524      */
25525     getConnection : function(){
25526         return this.useAjax ? Roo.Ajax : this.conn;
25527     },
25528
25529     /**
25530      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25531      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25532      * process that block using the passed callback.
25533      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25534      * for the request to the remote server.
25535      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25536      * object into a block of Roo.data.Records.
25537      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25538      * The function must be passed <ul>
25539      * <li>The Record block object</li>
25540      * <li>The "arg" argument from the load function</li>
25541      * <li>A boolean success indicator</li>
25542      * </ul>
25543      * @param {Object} scope The scope in which to call the callback
25544      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25545      */
25546     load : function(params, reader, callback, scope, arg){
25547         if(this.fireEvent("beforeload", this, params) !== false){
25548             var  o = {
25549                 params : params || {},
25550                 request: {
25551                     callback : callback,
25552                     scope : scope,
25553                     arg : arg
25554                 },
25555                 reader: reader,
25556                 callback : this.loadResponse,
25557                 scope: this
25558             };
25559             if(this.useAjax){
25560                 Roo.applyIf(o, this.conn);
25561                 if(this.activeRequest){
25562                     Roo.Ajax.abort(this.activeRequest);
25563                 }
25564                 this.activeRequest = Roo.Ajax.request(o);
25565             }else{
25566                 this.conn.request(o);
25567             }
25568         }else{
25569             callback.call(scope||this, null, arg, false);
25570         }
25571     },
25572
25573     // private
25574     loadResponse : function(o, success, response){
25575         delete this.activeRequest;
25576         if(!success){
25577             this.fireEvent("loadexception", this, o, response);
25578             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25579             return;
25580         }
25581         var result;
25582         try {
25583             result = o.reader.read(response);
25584         }catch(e){
25585             o.success = false;
25586             o.raw = { errorMsg : response.responseText };
25587             this.fireEvent("loadexception", this, o, response, e);
25588             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25589             return;
25590         }
25591         
25592         this.fireEvent("load", this, o, o.request.arg);
25593         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25594     },
25595
25596     // private
25597     update : function(dataSet){
25598
25599     },
25600
25601     // private
25602     updateResponse : function(dataSet){
25603
25604     }
25605 });/*
25606  * Based on:
25607  * Ext JS Library 1.1.1
25608  * Copyright(c) 2006-2007, Ext JS, LLC.
25609  *
25610  * Originally Released Under LGPL - original licence link has changed is not relivant.
25611  *
25612  * Fork - LGPL
25613  * <script type="text/javascript">
25614  */
25615
25616 /**
25617  * @class Roo.data.ScriptTagProxy
25618  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25619  * other than the originating domain of the running page.<br><br>
25620  * <p>
25621  * <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
25622  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25623  * <p>
25624  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25625  * source code that is used as the source inside a &lt;script> tag.<br><br>
25626  * <p>
25627  * In order for the browser to process the returned data, the server must wrap the data object
25628  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25629  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25630  * depending on whether the callback name was passed:
25631  * <p>
25632  * <pre><code>
25633 boolean scriptTag = false;
25634 String cb = request.getParameter("callback");
25635 if (cb != null) {
25636     scriptTag = true;
25637     response.setContentType("text/javascript");
25638 } else {
25639     response.setContentType("application/x-json");
25640 }
25641 Writer out = response.getWriter();
25642 if (scriptTag) {
25643     out.write(cb + "(");
25644 }
25645 out.print(dataBlock.toJsonString());
25646 if (scriptTag) {
25647     out.write(");");
25648 }
25649 </pre></code>
25650  *
25651  * @constructor
25652  * @param {Object} config A configuration object.
25653  */
25654 Roo.data.ScriptTagProxy = function(config){
25655     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25656     Roo.apply(this, config);
25657     this.head = document.getElementsByTagName("head")[0];
25658 };
25659
25660 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25661
25662 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25663     /**
25664      * @cfg {String} url The URL from which to request the data object.
25665      */
25666     /**
25667      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25668      */
25669     timeout : 30000,
25670     /**
25671      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25672      * the server the name of the callback function set up by the load call to process the returned data object.
25673      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25674      * javascript output which calls this named function passing the data object as its only parameter.
25675      */
25676     callbackParam : "callback",
25677     /**
25678      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25679      * name to the request.
25680      */
25681     nocache : true,
25682
25683     /**
25684      * Load data from the configured URL, read the data object into
25685      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25686      * process that block using the passed callback.
25687      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25688      * for the request to the remote server.
25689      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25690      * object into a block of Roo.data.Records.
25691      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25692      * The function must be passed <ul>
25693      * <li>The Record block object</li>
25694      * <li>The "arg" argument from the load function</li>
25695      * <li>A boolean success indicator</li>
25696      * </ul>
25697      * @param {Object} scope The scope in which to call the callback
25698      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25699      */
25700     load : function(params, reader, callback, scope, arg){
25701         if(this.fireEvent("beforeload", this, params) !== false){
25702
25703             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25704
25705             var url = this.url;
25706             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25707             if(this.nocache){
25708                 url += "&_dc=" + (new Date().getTime());
25709             }
25710             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25711             var trans = {
25712                 id : transId,
25713                 cb : "stcCallback"+transId,
25714                 scriptId : "stcScript"+transId,
25715                 params : params,
25716                 arg : arg,
25717                 url : url,
25718                 callback : callback,
25719                 scope : scope,
25720                 reader : reader
25721             };
25722             var conn = this;
25723
25724             window[trans.cb] = function(o){
25725                 conn.handleResponse(o, trans);
25726             };
25727
25728             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25729
25730             if(this.autoAbort !== false){
25731                 this.abort();
25732             }
25733
25734             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25735
25736             var script = document.createElement("script");
25737             script.setAttribute("src", url);
25738             script.setAttribute("type", "text/javascript");
25739             script.setAttribute("id", trans.scriptId);
25740             this.head.appendChild(script);
25741
25742             this.trans = trans;
25743         }else{
25744             callback.call(scope||this, null, arg, false);
25745         }
25746     },
25747
25748     // private
25749     isLoading : function(){
25750         return this.trans ? true : false;
25751     },
25752
25753     /**
25754      * Abort the current server request.
25755      */
25756     abort : function(){
25757         if(this.isLoading()){
25758             this.destroyTrans(this.trans);
25759         }
25760     },
25761
25762     // private
25763     destroyTrans : function(trans, isLoaded){
25764         this.head.removeChild(document.getElementById(trans.scriptId));
25765         clearTimeout(trans.timeoutId);
25766         if(isLoaded){
25767             window[trans.cb] = undefined;
25768             try{
25769                 delete window[trans.cb];
25770             }catch(e){}
25771         }else{
25772             // if hasn't been loaded, wait for load to remove it to prevent script error
25773             window[trans.cb] = function(){
25774                 window[trans.cb] = undefined;
25775                 try{
25776                     delete window[trans.cb];
25777                 }catch(e){}
25778             };
25779         }
25780     },
25781
25782     // private
25783     handleResponse : function(o, trans){
25784         this.trans = false;
25785         this.destroyTrans(trans, true);
25786         var result;
25787         try {
25788             result = trans.reader.readRecords(o);
25789         }catch(e){
25790             this.fireEvent("loadexception", this, o, trans.arg, e);
25791             trans.callback.call(trans.scope||window, null, trans.arg, false);
25792             return;
25793         }
25794         this.fireEvent("load", this, o, trans.arg);
25795         trans.callback.call(trans.scope||window, result, trans.arg, true);
25796     },
25797
25798     // private
25799     handleFailure : function(trans){
25800         this.trans = false;
25801         this.destroyTrans(trans, false);
25802         this.fireEvent("loadexception", this, null, trans.arg);
25803         trans.callback.call(trans.scope||window, null, trans.arg, false);
25804     }
25805 });/*
25806  * Based on:
25807  * Ext JS Library 1.1.1
25808  * Copyright(c) 2006-2007, Ext JS, LLC.
25809  *
25810  * Originally Released Under LGPL - original licence link has changed is not relivant.
25811  *
25812  * Fork - LGPL
25813  * <script type="text/javascript">
25814  */
25815
25816 /**
25817  * @class Roo.data.JsonReader
25818  * @extends Roo.data.DataReader
25819  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25820  * based on mappings in a provided Roo.data.Record constructor.
25821  * 
25822  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25823  * in the reply previously. 
25824  * 
25825  * <p>
25826  * Example code:
25827  * <pre><code>
25828 var RecordDef = Roo.data.Record.create([
25829     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25830     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25831 ]);
25832 var myReader = new Roo.data.JsonReader({
25833     totalProperty: "results",    // The property which contains the total dataset size (optional)
25834     root: "rows",                // The property which contains an Array of row objects
25835     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25836 }, RecordDef);
25837 </code></pre>
25838  * <p>
25839  * This would consume a JSON file like this:
25840  * <pre><code>
25841 { 'results': 2, 'rows': [
25842     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25843     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25844 }
25845 </code></pre>
25846  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25847  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25848  * paged from the remote server.
25849  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25850  * @cfg {String} root name of the property which contains the Array of row objects.
25851  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25852  * @cfg {Array} fields Array of field definition objects
25853  * @constructor
25854  * Create a new JsonReader
25855  * @param {Object} meta Metadata configuration options
25856  * @param {Object} recordType Either an Array of field definition objects,
25857  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25858  */
25859 Roo.data.JsonReader = function(meta, recordType){
25860     
25861     meta = meta || {};
25862     // set some defaults:
25863     Roo.applyIf(meta, {
25864         totalProperty: 'total',
25865         successProperty : 'success',
25866         root : 'data',
25867         id : 'id'
25868     });
25869     
25870     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25871 };
25872 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25873     
25874     readerType : 'Json',
25875     
25876     /**
25877      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25878      * Used by Store query builder to append _requestMeta to params.
25879      * 
25880      */
25881     metaFromRemote : false,
25882     /**
25883      * This method is only used by a DataProxy which has retrieved data from a remote server.
25884      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25885      * @return {Object} data A data block which is used by an Roo.data.Store object as
25886      * a cache of Roo.data.Records.
25887      */
25888     read : function(response){
25889         var json = response.responseText;
25890        
25891         var o = /* eval:var:o */ eval("("+json+")");
25892         if(!o) {
25893             throw {message: "JsonReader.read: Json object not found"};
25894         }
25895         
25896         if(o.metaData){
25897             
25898             delete this.ef;
25899             this.metaFromRemote = true;
25900             this.meta = o.metaData;
25901             this.recordType = Roo.data.Record.create(o.metaData.fields);
25902             this.onMetaChange(this.meta, this.recordType, o);
25903         }
25904         return this.readRecords(o);
25905     },
25906
25907     // private function a store will implement
25908     onMetaChange : function(meta, recordType, o){
25909
25910     },
25911
25912     /**
25913          * @ignore
25914          */
25915     simpleAccess: function(obj, subsc) {
25916         return obj[subsc];
25917     },
25918
25919         /**
25920          * @ignore
25921          */
25922     getJsonAccessor: function(){
25923         var re = /[\[\.]/;
25924         return function(expr) {
25925             try {
25926                 return(re.test(expr))
25927                     ? new Function("obj", "return obj." + expr)
25928                     : function(obj){
25929                         return obj[expr];
25930                     };
25931             } catch(e){}
25932             return Roo.emptyFn;
25933         };
25934     }(),
25935
25936     /**
25937      * Create a data block containing Roo.data.Records from an XML document.
25938      * @param {Object} o An object which contains an Array of row objects in the property specified
25939      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25940      * which contains the total size of the dataset.
25941      * @return {Object} data A data block which is used by an Roo.data.Store object as
25942      * a cache of Roo.data.Records.
25943      */
25944     readRecords : function(o){
25945         /**
25946          * After any data loads, the raw JSON data is available for further custom processing.
25947          * @type Object
25948          */
25949         this.o = o;
25950         var s = this.meta, Record = this.recordType,
25951             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25952
25953 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25954         if (!this.ef) {
25955             if(s.totalProperty) {
25956                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25957                 }
25958                 if(s.successProperty) {
25959                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25960                 }
25961                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25962                 if (s.id) {
25963                         var g = this.getJsonAccessor(s.id);
25964                         this.getId = function(rec) {
25965                                 var r = g(rec);  
25966                                 return (r === undefined || r === "") ? null : r;
25967                         };
25968                 } else {
25969                         this.getId = function(){return null;};
25970                 }
25971             this.ef = [];
25972             for(var jj = 0; jj < fl; jj++){
25973                 f = fi[jj];
25974                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25975                 this.ef[jj] = this.getJsonAccessor(map);
25976             }
25977         }
25978
25979         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25980         if(s.totalProperty){
25981             var vt = parseInt(this.getTotal(o), 10);
25982             if(!isNaN(vt)){
25983                 totalRecords = vt;
25984             }
25985         }
25986         if(s.successProperty){
25987             var vs = this.getSuccess(o);
25988             if(vs === false || vs === 'false'){
25989                 success = false;
25990             }
25991         }
25992         var records = [];
25993         for(var i = 0; i < c; i++){
25994             var n = root[i];
25995             var values = {};
25996             var id = this.getId(n);
25997             for(var j = 0; j < fl; j++){
25998                 f = fi[j];
25999                                 var v = this.ef[j](n);
26000                                 if (!f.convert) {
26001                                         Roo.log('missing convert for ' + f.name);
26002                                         Roo.log(f);
26003                                         continue;
26004                                 }
26005                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26006             }
26007                         if (!Record) {
26008                                 return {
26009                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26010                                         success : false,
26011                                         records : [],
26012                                         totalRecords : 0
26013                                 };
26014                         }
26015             var record = new Record(values, id);
26016             record.json = n;
26017             records[i] = record;
26018         }
26019         return {
26020             raw : o,
26021             success : success,
26022             records : records,
26023             totalRecords : totalRecords
26024         };
26025     },
26026     // used when loading children.. @see loadDataFromChildren
26027     toLoadData: function(rec)
26028     {
26029         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26030         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26031         return { data : data, total : data.length };
26032         
26033     }
26034 });/*
26035  * Based on:
26036  * Ext JS Library 1.1.1
26037  * Copyright(c) 2006-2007, Ext JS, LLC.
26038  *
26039  * Originally Released Under LGPL - original licence link has changed is not relivant.
26040  *
26041  * Fork - LGPL
26042  * <script type="text/javascript">
26043  */
26044
26045 /**
26046  * @class Roo.data.XmlReader
26047  * @extends Roo.data.DataReader
26048  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26049  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26050  * <p>
26051  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26052  * header in the HTTP response must be set to "text/xml".</em>
26053  * <p>
26054  * Example code:
26055  * <pre><code>
26056 var RecordDef = Roo.data.Record.create([
26057    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26058    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26059 ]);
26060 var myReader = new Roo.data.XmlReader({
26061    totalRecords: "results", // The element which contains the total dataset size (optional)
26062    record: "row",           // The repeated element which contains row information
26063    id: "id"                 // The element within the row that provides an ID for the record (optional)
26064 }, RecordDef);
26065 </code></pre>
26066  * <p>
26067  * This would consume an XML file like this:
26068  * <pre><code>
26069 &lt;?xml?>
26070 &lt;dataset>
26071  &lt;results>2&lt;/results>
26072  &lt;row>
26073    &lt;id>1&lt;/id>
26074    &lt;name>Bill&lt;/name>
26075    &lt;occupation>Gardener&lt;/occupation>
26076  &lt;/row>
26077  &lt;row>
26078    &lt;id>2&lt;/id>
26079    &lt;name>Ben&lt;/name>
26080    &lt;occupation>Horticulturalist&lt;/occupation>
26081  &lt;/row>
26082 &lt;/dataset>
26083 </code></pre>
26084  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26085  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26086  * paged from the remote server.
26087  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26088  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26089  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26090  * a record identifier value.
26091  * @constructor
26092  * Create a new XmlReader
26093  * @param {Object} meta Metadata configuration options
26094  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26095  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26096  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26097  */
26098 Roo.data.XmlReader = function(meta, recordType){
26099     meta = meta || {};
26100     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26101 };
26102 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26103     
26104     readerType : 'Xml',
26105     
26106     /**
26107      * This method is only used by a DataProxy which has retrieved data from a remote server.
26108          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26109          * to contain a method called 'responseXML' that returns an XML document object.
26110      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26111      * a cache of Roo.data.Records.
26112      */
26113     read : function(response){
26114         var doc = response.responseXML;
26115         if(!doc) {
26116             throw {message: "XmlReader.read: XML Document not available"};
26117         }
26118         return this.readRecords(doc);
26119     },
26120
26121     /**
26122      * Create a data block containing Roo.data.Records from an XML document.
26123          * @param {Object} doc A parsed XML document.
26124      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26125      * a cache of Roo.data.Records.
26126      */
26127     readRecords : function(doc){
26128         /**
26129          * After any data loads/reads, the raw XML Document is available for further custom processing.
26130          * @type XMLDocument
26131          */
26132         this.xmlData = doc;
26133         var root = doc.documentElement || doc;
26134         var q = Roo.DomQuery;
26135         var recordType = this.recordType, fields = recordType.prototype.fields;
26136         var sid = this.meta.id;
26137         var totalRecords = 0, success = true;
26138         if(this.meta.totalRecords){
26139             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26140         }
26141         
26142         if(this.meta.success){
26143             var sv = q.selectValue(this.meta.success, root, true);
26144             success = sv !== false && sv !== 'false';
26145         }
26146         var records = [];
26147         var ns = q.select(this.meta.record, root);
26148         for(var i = 0, len = ns.length; i < len; i++) {
26149                 var n = ns[i];
26150                 var values = {};
26151                 var id = sid ? q.selectValue(sid, n) : undefined;
26152                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26153                     var f = fields.items[j];
26154                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26155                     v = f.convert(v);
26156                     values[f.name] = v;
26157                 }
26158                 var record = new recordType(values, id);
26159                 record.node = n;
26160                 records[records.length] = record;
26161             }
26162
26163             return {
26164                 success : success,
26165                 records : records,
26166                 totalRecords : totalRecords || records.length
26167             };
26168     }
26169 });/*
26170  * Based on:
26171  * Ext JS Library 1.1.1
26172  * Copyright(c) 2006-2007, Ext JS, LLC.
26173  *
26174  * Originally Released Under LGPL - original licence link has changed is not relivant.
26175  *
26176  * Fork - LGPL
26177  * <script type="text/javascript">
26178  */
26179
26180 /**
26181  * @class Roo.data.ArrayReader
26182  * @extends Roo.data.DataReader
26183  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26184  * Each element of that Array represents a row of data fields. The
26185  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26186  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26187  * <p>
26188  * Example code:.
26189  * <pre><code>
26190 var RecordDef = Roo.data.Record.create([
26191     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26192     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26193 ]);
26194 var myReader = new Roo.data.ArrayReader({
26195     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26196 }, RecordDef);
26197 </code></pre>
26198  * <p>
26199  * This would consume an Array like this:
26200  * <pre><code>
26201 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26202   </code></pre>
26203  
26204  * @constructor
26205  * Create a new JsonReader
26206  * @param {Object} meta Metadata configuration options.
26207  * @param {Object|Array} recordType Either an Array of field definition objects
26208  * 
26209  * @cfg {Array} fields Array of field definition objects
26210  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26211  * as specified to {@link Roo.data.Record#create},
26212  * or an {@link Roo.data.Record} object
26213  *
26214  * 
26215  * created using {@link Roo.data.Record#create}.
26216  */
26217 Roo.data.ArrayReader = function(meta, recordType)
26218 {    
26219     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26220 };
26221
26222 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26223     
26224       /**
26225      * Create a data block containing Roo.data.Records from an XML document.
26226      * @param {Object} o An Array of row objects which represents the dataset.
26227      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26228      * a cache of Roo.data.Records.
26229      */
26230     readRecords : function(o)
26231     {
26232         var sid = this.meta ? this.meta.id : null;
26233         var recordType = this.recordType, fields = recordType.prototype.fields;
26234         var records = [];
26235         var root = o;
26236         for(var i = 0; i < root.length; i++){
26237             var n = root[i];
26238             var values = {};
26239             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26240             for(var j = 0, jlen = fields.length; j < jlen; j++){
26241                 var f = fields.items[j];
26242                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26243                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26244                 v = f.convert(v);
26245                 values[f.name] = v;
26246             }
26247             var record = new recordType(values, id);
26248             record.json = n;
26249             records[records.length] = record;
26250         }
26251         return {
26252             records : records,
26253             totalRecords : records.length
26254         };
26255     },
26256     // used when loading children.. @see loadDataFromChildren
26257     toLoadData: function(rec)
26258     {
26259         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26260         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26261         
26262     }
26263     
26264     
26265 });/*
26266  * Based on:
26267  * Ext JS Library 1.1.1
26268  * Copyright(c) 2006-2007, Ext JS, LLC.
26269  *
26270  * Originally Released Under LGPL - original licence link has changed is not relivant.
26271  *
26272  * Fork - LGPL
26273  * <script type="text/javascript">
26274  */
26275
26276
26277 /**
26278  * @class Roo.data.Tree
26279  * @extends Roo.util.Observable
26280  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26281  * in the tree have most standard DOM functionality.
26282  * @constructor
26283  * @param {Node} root (optional) The root node
26284  */
26285 Roo.data.Tree = function(root){
26286    this.nodeHash = {};
26287    /**
26288     * The root node for this tree
26289     * @type Node
26290     */
26291    this.root = null;
26292    if(root){
26293        this.setRootNode(root);
26294    }
26295    this.addEvents({
26296        /**
26297         * @event append
26298         * Fires when a new child node is appended to a node in this tree.
26299         * @param {Tree} tree The owner tree
26300         * @param {Node} parent The parent node
26301         * @param {Node} node The newly appended node
26302         * @param {Number} index The index of the newly appended node
26303         */
26304        "append" : true,
26305        /**
26306         * @event remove
26307         * Fires when a child node is removed from a node in this tree.
26308         * @param {Tree} tree The owner tree
26309         * @param {Node} parent The parent node
26310         * @param {Node} node The child node removed
26311         */
26312        "remove" : true,
26313        /**
26314         * @event move
26315         * Fires when a node is moved to a new location in the tree
26316         * @param {Tree} tree The owner tree
26317         * @param {Node} node The node moved
26318         * @param {Node} oldParent The old parent of this node
26319         * @param {Node} newParent The new parent of this node
26320         * @param {Number} index The index it was moved to
26321         */
26322        "move" : true,
26323        /**
26324         * @event insert
26325         * Fires when a new child node is inserted in a node in this tree.
26326         * @param {Tree} tree The owner tree
26327         * @param {Node} parent The parent node
26328         * @param {Node} node The child node inserted
26329         * @param {Node} refNode The child node the node was inserted before
26330         */
26331        "insert" : true,
26332        /**
26333         * @event beforeappend
26334         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26335         * @param {Tree} tree The owner tree
26336         * @param {Node} parent The parent node
26337         * @param {Node} node The child node to be appended
26338         */
26339        "beforeappend" : true,
26340        /**
26341         * @event beforeremove
26342         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26343         * @param {Tree} tree The owner tree
26344         * @param {Node} parent The parent node
26345         * @param {Node} node The child node to be removed
26346         */
26347        "beforeremove" : true,
26348        /**
26349         * @event beforemove
26350         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26351         * @param {Tree} tree The owner tree
26352         * @param {Node} node The node being moved
26353         * @param {Node} oldParent The parent of the node
26354         * @param {Node} newParent The new parent the node is moving to
26355         * @param {Number} index The index it is being moved to
26356         */
26357        "beforemove" : true,
26358        /**
26359         * @event beforeinsert
26360         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26361         * @param {Tree} tree The owner tree
26362         * @param {Node} parent The parent node
26363         * @param {Node} node The child node to be inserted
26364         * @param {Node} refNode The child node the node is being inserted before
26365         */
26366        "beforeinsert" : true
26367    });
26368
26369     Roo.data.Tree.superclass.constructor.call(this);
26370 };
26371
26372 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26373     pathSeparator: "/",
26374
26375     proxyNodeEvent : function(){
26376         return this.fireEvent.apply(this, arguments);
26377     },
26378
26379     /**
26380      * Returns the root node for this tree.
26381      * @return {Node}
26382      */
26383     getRootNode : function(){
26384         return this.root;
26385     },
26386
26387     /**
26388      * Sets the root node for this tree.
26389      * @param {Node} node
26390      * @return {Node}
26391      */
26392     setRootNode : function(node){
26393         this.root = node;
26394         node.ownerTree = this;
26395         node.isRoot = true;
26396         this.registerNode(node);
26397         return node;
26398     },
26399
26400     /**
26401      * Gets a node in this tree by its id.
26402      * @param {String} id
26403      * @return {Node}
26404      */
26405     getNodeById : function(id){
26406         return this.nodeHash[id];
26407     },
26408
26409     registerNode : function(node){
26410         this.nodeHash[node.id] = node;
26411     },
26412
26413     unregisterNode : function(node){
26414         delete this.nodeHash[node.id];
26415     },
26416
26417     toString : function(){
26418         return "[Tree"+(this.id?" "+this.id:"")+"]";
26419     }
26420 });
26421
26422 /**
26423  * @class Roo.data.Node
26424  * @extends Roo.util.Observable
26425  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26426  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26427  * @constructor
26428  * @param {Object} attributes The attributes/config for the node
26429  */
26430 Roo.data.Node = function(attributes){
26431     /**
26432      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26433      * @type {Object}
26434      */
26435     this.attributes = attributes || {};
26436     this.leaf = this.attributes.leaf;
26437     /**
26438      * The node id. @type String
26439      */
26440     this.id = this.attributes.id;
26441     if(!this.id){
26442         this.id = Roo.id(null, "ynode-");
26443         this.attributes.id = this.id;
26444     }
26445      
26446     
26447     /**
26448      * All child nodes of this node. @type Array
26449      */
26450     this.childNodes = [];
26451     if(!this.childNodes.indexOf){ // indexOf is a must
26452         this.childNodes.indexOf = function(o){
26453             for(var i = 0, len = this.length; i < len; i++){
26454                 if(this[i] == o) {
26455                     return i;
26456                 }
26457             }
26458             return -1;
26459         };
26460     }
26461     /**
26462      * The parent node for this node. @type Node
26463      */
26464     this.parentNode = null;
26465     /**
26466      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26467      */
26468     this.firstChild = null;
26469     /**
26470      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26471      */
26472     this.lastChild = null;
26473     /**
26474      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26475      */
26476     this.previousSibling = null;
26477     /**
26478      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26479      */
26480     this.nextSibling = null;
26481
26482     this.addEvents({
26483        /**
26484         * @event append
26485         * Fires when a new child node is appended
26486         * @param {Tree} tree The owner tree
26487         * @param {Node} this This node
26488         * @param {Node} node The newly appended node
26489         * @param {Number} index The index of the newly appended node
26490         */
26491        "append" : true,
26492        /**
26493         * @event remove
26494         * Fires when a child node is removed
26495         * @param {Tree} tree The owner tree
26496         * @param {Node} this This node
26497         * @param {Node} node The removed node
26498         */
26499        "remove" : true,
26500        /**
26501         * @event move
26502         * Fires when this node is moved to a new location in the tree
26503         * @param {Tree} tree The owner tree
26504         * @param {Node} this This node
26505         * @param {Node} oldParent The old parent of this node
26506         * @param {Node} newParent The new parent of this node
26507         * @param {Number} index The index it was moved to
26508         */
26509        "move" : true,
26510        /**
26511         * @event insert
26512         * Fires when a new child node is inserted.
26513         * @param {Tree} tree The owner tree
26514         * @param {Node} this This node
26515         * @param {Node} node The child node inserted
26516         * @param {Node} refNode The child node the node was inserted before
26517         */
26518        "insert" : true,
26519        /**
26520         * @event beforeappend
26521         * Fires before a new child is appended, return false to cancel the append.
26522         * @param {Tree} tree The owner tree
26523         * @param {Node} this This node
26524         * @param {Node} node The child node to be appended
26525         */
26526        "beforeappend" : true,
26527        /**
26528         * @event beforeremove
26529         * Fires before a child is removed, return false to cancel the remove.
26530         * @param {Tree} tree The owner tree
26531         * @param {Node} this This node
26532         * @param {Node} node The child node to be removed
26533         */
26534        "beforeremove" : true,
26535        /**
26536         * @event beforemove
26537         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26538         * @param {Tree} tree The owner tree
26539         * @param {Node} this This node
26540         * @param {Node} oldParent The parent of this node
26541         * @param {Node} newParent The new parent this node is moving to
26542         * @param {Number} index The index it is being moved to
26543         */
26544        "beforemove" : true,
26545        /**
26546         * @event beforeinsert
26547         * Fires before a new child is inserted, return false to cancel the insert.
26548         * @param {Tree} tree The owner tree
26549         * @param {Node} this This node
26550         * @param {Node} node The child node to be inserted
26551         * @param {Node} refNode The child node the node is being inserted before
26552         */
26553        "beforeinsert" : true
26554    });
26555     this.listeners = this.attributes.listeners;
26556     Roo.data.Node.superclass.constructor.call(this);
26557 };
26558
26559 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26560     fireEvent : function(evtName){
26561         // first do standard event for this node
26562         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26563             return false;
26564         }
26565         // then bubble it up to the tree if the event wasn't cancelled
26566         var ot = this.getOwnerTree();
26567         if(ot){
26568             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26569                 return false;
26570             }
26571         }
26572         return true;
26573     },
26574
26575     /**
26576      * Returns true if this node is a leaf
26577      * @return {Boolean}
26578      */
26579     isLeaf : function(){
26580         return this.leaf === true;
26581     },
26582
26583     // private
26584     setFirstChild : function(node){
26585         this.firstChild = node;
26586     },
26587
26588     //private
26589     setLastChild : function(node){
26590         this.lastChild = node;
26591     },
26592
26593
26594     /**
26595      * Returns true if this node is the last child of its parent
26596      * @return {Boolean}
26597      */
26598     isLast : function(){
26599        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26600     },
26601
26602     /**
26603      * Returns true if this node is the first child of its parent
26604      * @return {Boolean}
26605      */
26606     isFirst : function(){
26607        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26608     },
26609
26610     hasChildNodes : function(){
26611         return !this.isLeaf() && this.childNodes.length > 0;
26612     },
26613
26614     /**
26615      * Insert node(s) as the last child node of this node.
26616      * @param {Node/Array} node The node or Array of nodes to append
26617      * @return {Node} The appended node if single append, or null if an array was passed
26618      */
26619     appendChild : function(node){
26620         var multi = false;
26621         if(node instanceof Array){
26622             multi = node;
26623         }else if(arguments.length > 1){
26624             multi = arguments;
26625         }
26626         
26627         // if passed an array or multiple args do them one by one
26628         if(multi){
26629             for(var i = 0, len = multi.length; i < len; i++) {
26630                 this.appendChild(multi[i]);
26631             }
26632         }else{
26633             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26634                 return false;
26635             }
26636             var index = this.childNodes.length;
26637             var oldParent = node.parentNode;
26638             // it's a move, make sure we move it cleanly
26639             if(oldParent){
26640                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26641                     return false;
26642                 }
26643                 oldParent.removeChild(node);
26644             }
26645             
26646             index = this.childNodes.length;
26647             if(index == 0){
26648                 this.setFirstChild(node);
26649             }
26650             this.childNodes.push(node);
26651             node.parentNode = this;
26652             var ps = this.childNodes[index-1];
26653             if(ps){
26654                 node.previousSibling = ps;
26655                 ps.nextSibling = node;
26656             }else{
26657                 node.previousSibling = null;
26658             }
26659             node.nextSibling = null;
26660             this.setLastChild(node);
26661             node.setOwnerTree(this.getOwnerTree());
26662             this.fireEvent("append", this.ownerTree, this, node, index);
26663             if(this.ownerTree) {
26664                 this.ownerTree.fireEvent("appendnode", this, node, index);
26665             }
26666             if(oldParent){
26667                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26668             }
26669             return node;
26670         }
26671     },
26672
26673     /**
26674      * Removes a child node from this node.
26675      * @param {Node} node The node to remove
26676      * @return {Node} The removed node
26677      */
26678     removeChild : function(node){
26679         var index = this.childNodes.indexOf(node);
26680         if(index == -1){
26681             return false;
26682         }
26683         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26684             return false;
26685         }
26686
26687         // remove it from childNodes collection
26688         this.childNodes.splice(index, 1);
26689
26690         // update siblings
26691         if(node.previousSibling){
26692             node.previousSibling.nextSibling = node.nextSibling;
26693         }
26694         if(node.nextSibling){
26695             node.nextSibling.previousSibling = node.previousSibling;
26696         }
26697
26698         // update child refs
26699         if(this.firstChild == node){
26700             this.setFirstChild(node.nextSibling);
26701         }
26702         if(this.lastChild == node){
26703             this.setLastChild(node.previousSibling);
26704         }
26705
26706         node.setOwnerTree(null);
26707         // clear any references from the node
26708         node.parentNode = null;
26709         node.previousSibling = null;
26710         node.nextSibling = null;
26711         this.fireEvent("remove", this.ownerTree, this, node);
26712         return node;
26713     },
26714
26715     /**
26716      * Inserts the first node before the second node in this nodes childNodes collection.
26717      * @param {Node} node The node to insert
26718      * @param {Node} refNode The node to insert before (if null the node is appended)
26719      * @return {Node} The inserted node
26720      */
26721     insertBefore : function(node, refNode){
26722         if(!refNode){ // like standard Dom, refNode can be null for append
26723             return this.appendChild(node);
26724         }
26725         // nothing to do
26726         if(node == refNode){
26727             return false;
26728         }
26729
26730         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26731             return false;
26732         }
26733         var index = this.childNodes.indexOf(refNode);
26734         var oldParent = node.parentNode;
26735         var refIndex = index;
26736
26737         // when moving internally, indexes will change after remove
26738         if(oldParent == this && this.childNodes.indexOf(node) < index){
26739             refIndex--;
26740         }
26741
26742         // it's a move, make sure we move it cleanly
26743         if(oldParent){
26744             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26745                 return false;
26746             }
26747             oldParent.removeChild(node);
26748         }
26749         if(refIndex == 0){
26750             this.setFirstChild(node);
26751         }
26752         this.childNodes.splice(refIndex, 0, node);
26753         node.parentNode = this;
26754         var ps = this.childNodes[refIndex-1];
26755         if(ps){
26756             node.previousSibling = ps;
26757             ps.nextSibling = node;
26758         }else{
26759             node.previousSibling = null;
26760         }
26761         node.nextSibling = refNode;
26762         refNode.previousSibling = node;
26763         node.setOwnerTree(this.getOwnerTree());
26764         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26765         if(oldParent){
26766             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26767         }
26768         return node;
26769     },
26770
26771     /**
26772      * Returns the child node at the specified index.
26773      * @param {Number} index
26774      * @return {Node}
26775      */
26776     item : function(index){
26777         return this.childNodes[index];
26778     },
26779
26780     /**
26781      * Replaces one child node in this node with another.
26782      * @param {Node} newChild The replacement node
26783      * @param {Node} oldChild The node to replace
26784      * @return {Node} The replaced node
26785      */
26786     replaceChild : function(newChild, oldChild){
26787         this.insertBefore(newChild, oldChild);
26788         this.removeChild(oldChild);
26789         return oldChild;
26790     },
26791
26792     /**
26793      * Returns the index of a child node
26794      * @param {Node} node
26795      * @return {Number} The index of the node or -1 if it was not found
26796      */
26797     indexOf : function(child){
26798         return this.childNodes.indexOf(child);
26799     },
26800
26801     /**
26802      * Returns the tree this node is in.
26803      * @return {Tree}
26804      */
26805     getOwnerTree : function(){
26806         // if it doesn't have one, look for one
26807         if(!this.ownerTree){
26808             var p = this;
26809             while(p){
26810                 if(p.ownerTree){
26811                     this.ownerTree = p.ownerTree;
26812                     break;
26813                 }
26814                 p = p.parentNode;
26815             }
26816         }
26817         return this.ownerTree;
26818     },
26819
26820     /**
26821      * Returns depth of this node (the root node has a depth of 0)
26822      * @return {Number}
26823      */
26824     getDepth : function(){
26825         var depth = 0;
26826         var p = this;
26827         while(p.parentNode){
26828             ++depth;
26829             p = p.parentNode;
26830         }
26831         return depth;
26832     },
26833
26834     // private
26835     setOwnerTree : function(tree){
26836         // if it's move, we need to update everyone
26837         if(tree != this.ownerTree){
26838             if(this.ownerTree){
26839                 this.ownerTree.unregisterNode(this);
26840             }
26841             this.ownerTree = tree;
26842             var cs = this.childNodes;
26843             for(var i = 0, len = cs.length; i < len; i++) {
26844                 cs[i].setOwnerTree(tree);
26845             }
26846             if(tree){
26847                 tree.registerNode(this);
26848             }
26849         }
26850     },
26851
26852     /**
26853      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26854      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26855      * @return {String} The path
26856      */
26857     getPath : function(attr){
26858         attr = attr || "id";
26859         var p = this.parentNode;
26860         var b = [this.attributes[attr]];
26861         while(p){
26862             b.unshift(p.attributes[attr]);
26863             p = p.parentNode;
26864         }
26865         var sep = this.getOwnerTree().pathSeparator;
26866         return sep + b.join(sep);
26867     },
26868
26869     /**
26870      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26871      * function call will be the scope provided or the current node. The arguments to the function
26872      * will be the args provided or the current node. If the function returns false at any point,
26873      * the bubble is stopped.
26874      * @param {Function} fn The function to call
26875      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26876      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26877      */
26878     bubble : function(fn, scope, args){
26879         var p = this;
26880         while(p){
26881             if(fn.call(scope || p, args || p) === false){
26882                 break;
26883             }
26884             p = p.parentNode;
26885         }
26886     },
26887
26888     /**
26889      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26890      * function call will be the scope provided or the current node. The arguments to the function
26891      * will be the args provided or the current node. If the function returns false at any point,
26892      * the cascade is stopped on that branch.
26893      * @param {Function} fn The function to call
26894      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26895      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26896      */
26897     cascade : function(fn, scope, args){
26898         if(fn.call(scope || this, args || this) !== false){
26899             var cs = this.childNodes;
26900             for(var i = 0, len = cs.length; i < len; i++) {
26901                 cs[i].cascade(fn, scope, args);
26902             }
26903         }
26904     },
26905
26906     /**
26907      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26908      * function call will be the scope provided or the current node. The arguments to the function
26909      * will be the args provided or the current node. If the function returns false at any point,
26910      * the iteration stops.
26911      * @param {Function} fn The function to call
26912      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26913      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26914      */
26915     eachChild : function(fn, scope, args){
26916         var cs = this.childNodes;
26917         for(var i = 0, len = cs.length; i < len; i++) {
26918                 if(fn.call(scope || this, args || cs[i]) === false){
26919                     break;
26920                 }
26921         }
26922     },
26923
26924     /**
26925      * Finds the first child that has the attribute with the specified value.
26926      * @param {String} attribute The attribute name
26927      * @param {Mixed} value The value to search for
26928      * @return {Node} The found child or null if none was found
26929      */
26930     findChild : function(attribute, value){
26931         var cs = this.childNodes;
26932         for(var i = 0, len = cs.length; i < len; i++) {
26933                 if(cs[i].attributes[attribute] == value){
26934                     return cs[i];
26935                 }
26936         }
26937         return null;
26938     },
26939
26940     /**
26941      * Finds the first child by a custom function. The child matches if the function passed
26942      * returns true.
26943      * @param {Function} fn
26944      * @param {Object} scope (optional)
26945      * @return {Node} The found child or null if none was found
26946      */
26947     findChildBy : function(fn, scope){
26948         var cs = this.childNodes;
26949         for(var i = 0, len = cs.length; i < len; i++) {
26950                 if(fn.call(scope||cs[i], cs[i]) === true){
26951                     return cs[i];
26952                 }
26953         }
26954         return null;
26955     },
26956
26957     /**
26958      * Sorts this nodes children using the supplied sort function
26959      * @param {Function} fn
26960      * @param {Object} scope (optional)
26961      */
26962     sort : function(fn, scope){
26963         var cs = this.childNodes;
26964         var len = cs.length;
26965         if(len > 0){
26966             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26967             cs.sort(sortFn);
26968             for(var i = 0; i < len; i++){
26969                 var n = cs[i];
26970                 n.previousSibling = cs[i-1];
26971                 n.nextSibling = cs[i+1];
26972                 if(i == 0){
26973                     this.setFirstChild(n);
26974                 }
26975                 if(i == len-1){
26976                     this.setLastChild(n);
26977                 }
26978             }
26979         }
26980     },
26981
26982     /**
26983      * Returns true if this node is an ancestor (at any point) of the passed node.
26984      * @param {Node} node
26985      * @return {Boolean}
26986      */
26987     contains : function(node){
26988         return node.isAncestor(this);
26989     },
26990
26991     /**
26992      * Returns true if the passed node is an ancestor (at any point) of this node.
26993      * @param {Node} node
26994      * @return {Boolean}
26995      */
26996     isAncestor : function(node){
26997         var p = this.parentNode;
26998         while(p){
26999             if(p == node){
27000                 return true;
27001             }
27002             p = p.parentNode;
27003         }
27004         return false;
27005     },
27006
27007     toString : function(){
27008         return "[Node"+(this.id?" "+this.id:"")+"]";
27009     }
27010 });/*
27011  * Based on:
27012  * Ext JS Library 1.1.1
27013  * Copyright(c) 2006-2007, Ext JS, LLC.
27014  *
27015  * Originally Released Under LGPL - original licence link has changed is not relivant.
27016  *
27017  * Fork - LGPL
27018  * <script type="text/javascript">
27019  */
27020
27021
27022 /**
27023  * @class Roo.Shadow
27024  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27025  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27026  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27027  * @constructor
27028  * Create a new Shadow
27029  * @param {Object} config The config object
27030  */
27031 Roo.Shadow = function(config){
27032     Roo.apply(this, config);
27033     if(typeof this.mode != "string"){
27034         this.mode = this.defaultMode;
27035     }
27036     var o = this.offset, a = {h: 0};
27037     var rad = Math.floor(this.offset/2);
27038     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27039         case "drop":
27040             a.w = 0;
27041             a.l = a.t = o;
27042             a.t -= 1;
27043             if(Roo.isIE){
27044                 a.l -= this.offset + rad;
27045                 a.t -= this.offset + rad;
27046                 a.w -= rad;
27047                 a.h -= rad;
27048                 a.t += 1;
27049             }
27050         break;
27051         case "sides":
27052             a.w = (o*2);
27053             a.l = -o;
27054             a.t = o-1;
27055             if(Roo.isIE){
27056                 a.l -= (this.offset - rad);
27057                 a.t -= this.offset + rad;
27058                 a.l += 1;
27059                 a.w -= (this.offset - rad)*2;
27060                 a.w -= rad + 1;
27061                 a.h -= 1;
27062             }
27063         break;
27064         case "frame":
27065             a.w = a.h = (o*2);
27066             a.l = a.t = -o;
27067             a.t += 1;
27068             a.h -= 2;
27069             if(Roo.isIE){
27070                 a.l -= (this.offset - rad);
27071                 a.t -= (this.offset - rad);
27072                 a.l += 1;
27073                 a.w -= (this.offset + rad + 1);
27074                 a.h -= (this.offset + rad);
27075                 a.h += 1;
27076             }
27077         break;
27078     };
27079
27080     this.adjusts = a;
27081 };
27082
27083 Roo.Shadow.prototype = {
27084     /**
27085      * @cfg {String} mode
27086      * The shadow display mode.  Supports the following options:<br />
27087      * sides: Shadow displays on both sides and bottom only<br />
27088      * frame: Shadow displays equally on all four sides<br />
27089      * drop: Traditional bottom-right drop shadow (default)
27090      */
27091     mode: false,
27092     /**
27093      * @cfg {String} offset
27094      * The number of pixels to offset the shadow from the element (defaults to 4)
27095      */
27096     offset: 4,
27097
27098     // private
27099     defaultMode: "drop",
27100
27101     /**
27102      * Displays the shadow under the target element
27103      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27104      */
27105     show : function(target){
27106         target = Roo.get(target);
27107         if(!this.el){
27108             this.el = Roo.Shadow.Pool.pull();
27109             if(this.el.dom.nextSibling != target.dom){
27110                 this.el.insertBefore(target);
27111             }
27112         }
27113         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27114         if(Roo.isIE){
27115             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27116         }
27117         this.realign(
27118             target.getLeft(true),
27119             target.getTop(true),
27120             target.getWidth(),
27121             target.getHeight()
27122         );
27123         this.el.dom.style.display = "block";
27124     },
27125
27126     /**
27127      * Returns true if the shadow is visible, else false
27128      */
27129     isVisible : function(){
27130         return this.el ? true : false;  
27131     },
27132
27133     /**
27134      * Direct alignment when values are already available. Show must be called at least once before
27135      * calling this method to ensure it is initialized.
27136      * @param {Number} left The target element left position
27137      * @param {Number} top The target element top position
27138      * @param {Number} width The target element width
27139      * @param {Number} height The target element height
27140      */
27141     realign : function(l, t, w, h){
27142         if(!this.el){
27143             return;
27144         }
27145         var a = this.adjusts, d = this.el.dom, s = d.style;
27146         var iea = 0;
27147         s.left = (l+a.l)+"px";
27148         s.top = (t+a.t)+"px";
27149         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27150  
27151         if(s.width != sws || s.height != shs){
27152             s.width = sws;
27153             s.height = shs;
27154             if(!Roo.isIE){
27155                 var cn = d.childNodes;
27156                 var sww = Math.max(0, (sw-12))+"px";
27157                 cn[0].childNodes[1].style.width = sww;
27158                 cn[1].childNodes[1].style.width = sww;
27159                 cn[2].childNodes[1].style.width = sww;
27160                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27161             }
27162         }
27163     },
27164
27165     /**
27166      * Hides this shadow
27167      */
27168     hide : function(){
27169         if(this.el){
27170             this.el.dom.style.display = "none";
27171             Roo.Shadow.Pool.push(this.el);
27172             delete this.el;
27173         }
27174     },
27175
27176     /**
27177      * Adjust the z-index of this shadow
27178      * @param {Number} zindex The new z-index
27179      */
27180     setZIndex : function(z){
27181         this.zIndex = z;
27182         if(this.el){
27183             this.el.setStyle("z-index", z);
27184         }
27185     }
27186 };
27187
27188 // Private utility class that manages the internal Shadow cache
27189 Roo.Shadow.Pool = function(){
27190     var p = [];
27191     var markup = Roo.isIE ?
27192                  '<div class="x-ie-shadow"></div>' :
27193                  '<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>';
27194     return {
27195         pull : function(){
27196             var sh = p.shift();
27197             if(!sh){
27198                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27199                 sh.autoBoxAdjust = false;
27200             }
27201             return sh;
27202         },
27203
27204         push : function(sh){
27205             p.push(sh);
27206         }
27207     };
27208 }();/*
27209  * Based on:
27210  * Ext JS Library 1.1.1
27211  * Copyright(c) 2006-2007, Ext JS, LLC.
27212  *
27213  * Originally Released Under LGPL - original licence link has changed is not relivant.
27214  *
27215  * Fork - LGPL
27216  * <script type="text/javascript">
27217  */
27218
27219
27220 /**
27221  * @class Roo.SplitBar
27222  * @extends Roo.util.Observable
27223  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27224  * <br><br>
27225  * Usage:
27226  * <pre><code>
27227 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27228                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27229 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27230 split.minSize = 100;
27231 split.maxSize = 600;
27232 split.animate = true;
27233 split.on('moved', splitterMoved);
27234 </code></pre>
27235  * @constructor
27236  * Create a new SplitBar
27237  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27238  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27239  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27240  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27241                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27242                         position of the SplitBar).
27243  */
27244 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27245     
27246     /** @private */
27247     this.el = Roo.get(dragElement, true);
27248     this.el.dom.unselectable = "on";
27249     /** @private */
27250     this.resizingEl = Roo.get(resizingElement, true);
27251
27252     /**
27253      * @private
27254      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27255      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27256      * @type Number
27257      */
27258     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27259     
27260     /**
27261      * The minimum size of the resizing element. (Defaults to 0)
27262      * @type Number
27263      */
27264     this.minSize = 0;
27265     
27266     /**
27267      * The maximum size of the resizing element. (Defaults to 2000)
27268      * @type Number
27269      */
27270     this.maxSize = 2000;
27271     
27272     /**
27273      * Whether to animate the transition to the new size
27274      * @type Boolean
27275      */
27276     this.animate = false;
27277     
27278     /**
27279      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27280      * @type Boolean
27281      */
27282     this.useShim = false;
27283     
27284     /** @private */
27285     this.shim = null;
27286     
27287     if(!existingProxy){
27288         /** @private */
27289         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27290     }else{
27291         this.proxy = Roo.get(existingProxy).dom;
27292     }
27293     /** @private */
27294     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27295     
27296     /** @private */
27297     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27298     
27299     /** @private */
27300     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27301     
27302     /** @private */
27303     this.dragSpecs = {};
27304     
27305     /**
27306      * @private The adapter to use to positon and resize elements
27307      */
27308     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27309     this.adapter.init(this);
27310     
27311     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27312         /** @private */
27313         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27314         this.el.addClass("x-splitbar-h");
27315     }else{
27316         /** @private */
27317         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27318         this.el.addClass("x-splitbar-v");
27319     }
27320     
27321     this.addEvents({
27322         /**
27323          * @event resize
27324          * Fires when the splitter is moved (alias for {@link #event-moved})
27325          * @param {Roo.SplitBar} this
27326          * @param {Number} newSize the new width or height
27327          */
27328         "resize" : true,
27329         /**
27330          * @event moved
27331          * Fires when the splitter is moved
27332          * @param {Roo.SplitBar} this
27333          * @param {Number} newSize the new width or height
27334          */
27335         "moved" : true,
27336         /**
27337          * @event beforeresize
27338          * Fires before the splitter is dragged
27339          * @param {Roo.SplitBar} this
27340          */
27341         "beforeresize" : true,
27342
27343         "beforeapply" : true
27344     });
27345
27346     Roo.util.Observable.call(this);
27347 };
27348
27349 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27350     onStartProxyDrag : function(x, y){
27351         this.fireEvent("beforeresize", this);
27352         if(!this.overlay){
27353             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27354             o.unselectable();
27355             o.enableDisplayMode("block");
27356             // all splitbars share the same overlay
27357             Roo.SplitBar.prototype.overlay = o;
27358         }
27359         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27360         this.overlay.show();
27361         Roo.get(this.proxy).setDisplayed("block");
27362         var size = this.adapter.getElementSize(this);
27363         this.activeMinSize = this.getMinimumSize();;
27364         this.activeMaxSize = this.getMaximumSize();;
27365         var c1 = size - this.activeMinSize;
27366         var c2 = Math.max(this.activeMaxSize - size, 0);
27367         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27368             this.dd.resetConstraints();
27369             this.dd.setXConstraint(
27370                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27371                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27372             );
27373             this.dd.setYConstraint(0, 0);
27374         }else{
27375             this.dd.resetConstraints();
27376             this.dd.setXConstraint(0, 0);
27377             this.dd.setYConstraint(
27378                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27379                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27380             );
27381          }
27382         this.dragSpecs.startSize = size;
27383         this.dragSpecs.startPoint = [x, y];
27384         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27385     },
27386     
27387     /** 
27388      * @private Called after the drag operation by the DDProxy
27389      */
27390     onEndProxyDrag : function(e){
27391         Roo.get(this.proxy).setDisplayed(false);
27392         var endPoint = Roo.lib.Event.getXY(e);
27393         if(this.overlay){
27394             this.overlay.hide();
27395         }
27396         var newSize;
27397         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27398             newSize = this.dragSpecs.startSize + 
27399                 (this.placement == Roo.SplitBar.LEFT ?
27400                     endPoint[0] - this.dragSpecs.startPoint[0] :
27401                     this.dragSpecs.startPoint[0] - endPoint[0]
27402                 );
27403         }else{
27404             newSize = this.dragSpecs.startSize + 
27405                 (this.placement == Roo.SplitBar.TOP ?
27406                     endPoint[1] - this.dragSpecs.startPoint[1] :
27407                     this.dragSpecs.startPoint[1] - endPoint[1]
27408                 );
27409         }
27410         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27411         if(newSize != this.dragSpecs.startSize){
27412             if(this.fireEvent('beforeapply', this, newSize) !== false){
27413                 this.adapter.setElementSize(this, newSize);
27414                 this.fireEvent("moved", this, newSize);
27415                 this.fireEvent("resize", this, newSize);
27416             }
27417         }
27418     },
27419     
27420     /**
27421      * Get the adapter this SplitBar uses
27422      * @return The adapter object
27423      */
27424     getAdapter : function(){
27425         return this.adapter;
27426     },
27427     
27428     /**
27429      * Set the adapter this SplitBar uses
27430      * @param {Object} adapter A SplitBar adapter object
27431      */
27432     setAdapter : function(adapter){
27433         this.adapter = adapter;
27434         this.adapter.init(this);
27435     },
27436     
27437     /**
27438      * Gets the minimum size for the resizing element
27439      * @return {Number} The minimum size
27440      */
27441     getMinimumSize : function(){
27442         return this.minSize;
27443     },
27444     
27445     /**
27446      * Sets the minimum size for the resizing element
27447      * @param {Number} minSize The minimum size
27448      */
27449     setMinimumSize : function(minSize){
27450         this.minSize = minSize;
27451     },
27452     
27453     /**
27454      * Gets the maximum size for the resizing element
27455      * @return {Number} The maximum size
27456      */
27457     getMaximumSize : function(){
27458         return this.maxSize;
27459     },
27460     
27461     /**
27462      * Sets the maximum size for the resizing element
27463      * @param {Number} maxSize The maximum size
27464      */
27465     setMaximumSize : function(maxSize){
27466         this.maxSize = maxSize;
27467     },
27468     
27469     /**
27470      * Sets the initialize size for the resizing element
27471      * @param {Number} size The initial size
27472      */
27473     setCurrentSize : function(size){
27474         var oldAnimate = this.animate;
27475         this.animate = false;
27476         this.adapter.setElementSize(this, size);
27477         this.animate = oldAnimate;
27478     },
27479     
27480     /**
27481      * Destroy this splitbar. 
27482      * @param {Boolean} removeEl True to remove the element
27483      */
27484     destroy : function(removeEl){
27485         if(this.shim){
27486             this.shim.remove();
27487         }
27488         this.dd.unreg();
27489         this.proxy.parentNode.removeChild(this.proxy);
27490         if(removeEl){
27491             this.el.remove();
27492         }
27493     }
27494 });
27495
27496 /**
27497  * @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.
27498  */
27499 Roo.SplitBar.createProxy = function(dir){
27500     var proxy = new Roo.Element(document.createElement("div"));
27501     proxy.unselectable();
27502     var cls = 'x-splitbar-proxy';
27503     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27504     document.body.appendChild(proxy.dom);
27505     return proxy.dom;
27506 };
27507
27508 /** 
27509  * @class Roo.SplitBar.BasicLayoutAdapter
27510  * Default Adapter. It assumes the splitter and resizing element are not positioned
27511  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27512  */
27513 Roo.SplitBar.BasicLayoutAdapter = function(){
27514 };
27515
27516 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27517     // do nothing for now
27518     init : function(s){
27519     
27520     },
27521     /**
27522      * Called before drag operations to get the current size of the resizing element. 
27523      * @param {Roo.SplitBar} s The SplitBar using this adapter
27524      */
27525      getElementSize : function(s){
27526         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27527             return s.resizingEl.getWidth();
27528         }else{
27529             return s.resizingEl.getHeight();
27530         }
27531     },
27532     
27533     /**
27534      * Called after drag operations to set the size of the resizing element.
27535      * @param {Roo.SplitBar} s The SplitBar using this adapter
27536      * @param {Number} newSize The new size to set
27537      * @param {Function} onComplete A function to be invoked when resizing is complete
27538      */
27539     setElementSize : function(s, newSize, onComplete){
27540         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27541             if(!s.animate){
27542                 s.resizingEl.setWidth(newSize);
27543                 if(onComplete){
27544                     onComplete(s, newSize);
27545                 }
27546             }else{
27547                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27548             }
27549         }else{
27550             
27551             if(!s.animate){
27552                 s.resizingEl.setHeight(newSize);
27553                 if(onComplete){
27554                     onComplete(s, newSize);
27555                 }
27556             }else{
27557                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27558             }
27559         }
27560     }
27561 };
27562
27563 /** 
27564  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27565  * @extends Roo.SplitBar.BasicLayoutAdapter
27566  * Adapter that  moves the splitter element to align with the resized sizing element. 
27567  * Used with an absolute positioned SplitBar.
27568  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27569  * document.body, make sure you assign an id to the body element.
27570  */
27571 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27572     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27573     this.container = Roo.get(container);
27574 };
27575
27576 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27577     init : function(s){
27578         this.basic.init(s);
27579     },
27580     
27581     getElementSize : function(s){
27582         return this.basic.getElementSize(s);
27583     },
27584     
27585     setElementSize : function(s, newSize, onComplete){
27586         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27587     },
27588     
27589     moveSplitter : function(s){
27590         var yes = Roo.SplitBar;
27591         switch(s.placement){
27592             case yes.LEFT:
27593                 s.el.setX(s.resizingEl.getRight());
27594                 break;
27595             case yes.RIGHT:
27596                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27597                 break;
27598             case yes.TOP:
27599                 s.el.setY(s.resizingEl.getBottom());
27600                 break;
27601             case yes.BOTTOM:
27602                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27603                 break;
27604         }
27605     }
27606 };
27607
27608 /**
27609  * Orientation constant - Create a vertical SplitBar
27610  * @static
27611  * @type Number
27612  */
27613 Roo.SplitBar.VERTICAL = 1;
27614
27615 /**
27616  * Orientation constant - Create a horizontal SplitBar
27617  * @static
27618  * @type Number
27619  */
27620 Roo.SplitBar.HORIZONTAL = 2;
27621
27622 /**
27623  * Placement constant - The resizing element is to the left of the splitter element
27624  * @static
27625  * @type Number
27626  */
27627 Roo.SplitBar.LEFT = 1;
27628
27629 /**
27630  * Placement constant - The resizing element is to the right of the splitter element
27631  * @static
27632  * @type Number
27633  */
27634 Roo.SplitBar.RIGHT = 2;
27635
27636 /**
27637  * Placement constant - The resizing element is positioned above the splitter element
27638  * @static
27639  * @type Number
27640  */
27641 Roo.SplitBar.TOP = 3;
27642
27643 /**
27644  * Placement constant - The resizing element is positioned under splitter element
27645  * @static
27646  * @type Number
27647  */
27648 Roo.SplitBar.BOTTOM = 4;
27649 /*
27650  * Based on:
27651  * Ext JS Library 1.1.1
27652  * Copyright(c) 2006-2007, Ext JS, LLC.
27653  *
27654  * Originally Released Under LGPL - original licence link has changed is not relivant.
27655  *
27656  * Fork - LGPL
27657  * <script type="text/javascript">
27658  */
27659
27660 /**
27661  * @class Roo.View
27662  * @extends Roo.util.Observable
27663  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27664  * This class also supports single and multi selection modes. <br>
27665  * Create a data model bound view:
27666  <pre><code>
27667  var store = new Roo.data.Store(...);
27668
27669  var view = new Roo.View({
27670     el : "my-element",
27671     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27672  
27673     singleSelect: true,
27674     selectedClass: "ydataview-selected",
27675     store: store
27676  });
27677
27678  // listen for node click?
27679  view.on("click", function(vw, index, node, e){
27680  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27681  });
27682
27683  // load XML data
27684  dataModel.load("foobar.xml");
27685  </code></pre>
27686  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27687  * <br><br>
27688  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27689  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27690  * 
27691  * Note: old style constructor is still suported (container, template, config)
27692  * 
27693  * @constructor
27694  * Create a new View
27695  * @param {Object} config The config object
27696  * 
27697  */
27698 Roo.View = function(config, depreciated_tpl, depreciated_config){
27699     
27700     this.parent = false;
27701     
27702     if (typeof(depreciated_tpl) == 'undefined') {
27703         // new way.. - universal constructor.
27704         Roo.apply(this, config);
27705         this.el  = Roo.get(this.el);
27706     } else {
27707         // old format..
27708         this.el  = Roo.get(config);
27709         this.tpl = depreciated_tpl;
27710         Roo.apply(this, depreciated_config);
27711     }
27712     this.wrapEl  = this.el.wrap().wrap();
27713     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27714     
27715     
27716     if(typeof(this.tpl) == "string"){
27717         this.tpl = new Roo.Template(this.tpl);
27718     } else {
27719         // support xtype ctors..
27720         this.tpl = new Roo.factory(this.tpl, Roo);
27721     }
27722     
27723     
27724     this.tpl.compile();
27725     
27726     /** @private */
27727     this.addEvents({
27728         /**
27729          * @event beforeclick
27730          * Fires before a click is processed. Returns false to cancel the default action.
27731          * @param {Roo.View} this
27732          * @param {Number} index The index of the target node
27733          * @param {HTMLElement} node The target node
27734          * @param {Roo.EventObject} e The raw event object
27735          */
27736             "beforeclick" : true,
27737         /**
27738          * @event click
27739          * Fires when a template node is clicked.
27740          * @param {Roo.View} this
27741          * @param {Number} index The index of the target node
27742          * @param {HTMLElement} node The target node
27743          * @param {Roo.EventObject} e The raw event object
27744          */
27745             "click" : true,
27746         /**
27747          * @event dblclick
27748          * Fires when a template node is double clicked.
27749          * @param {Roo.View} this
27750          * @param {Number} index The index of the target node
27751          * @param {HTMLElement} node The target node
27752          * @param {Roo.EventObject} e The raw event object
27753          */
27754             "dblclick" : true,
27755         /**
27756          * @event contextmenu
27757          * Fires when a template node is right clicked.
27758          * @param {Roo.View} this
27759          * @param {Number} index The index of the target node
27760          * @param {HTMLElement} node The target node
27761          * @param {Roo.EventObject} e The raw event object
27762          */
27763             "contextmenu" : true,
27764         /**
27765          * @event selectionchange
27766          * Fires when the selected nodes change.
27767          * @param {Roo.View} this
27768          * @param {Array} selections Array of the selected nodes
27769          */
27770             "selectionchange" : true,
27771     
27772         /**
27773          * @event beforeselect
27774          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27775          * @param {Roo.View} this
27776          * @param {HTMLElement} node The node to be selected
27777          * @param {Array} selections Array of currently selected nodes
27778          */
27779             "beforeselect" : true,
27780         /**
27781          * @event preparedata
27782          * Fires on every row to render, to allow you to change the data.
27783          * @param {Roo.View} this
27784          * @param {Object} data to be rendered (change this)
27785          */
27786           "preparedata" : true
27787           
27788           
27789         });
27790
27791
27792
27793     this.el.on({
27794         "click": this.onClick,
27795         "dblclick": this.onDblClick,
27796         "contextmenu": this.onContextMenu,
27797         scope:this
27798     });
27799
27800     this.selections = [];
27801     this.nodes = [];
27802     this.cmp = new Roo.CompositeElementLite([]);
27803     if(this.store){
27804         this.store = Roo.factory(this.store, Roo.data);
27805         this.setStore(this.store, true);
27806     }
27807     
27808     if ( this.footer && this.footer.xtype) {
27809            
27810          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27811         
27812         this.footer.dataSource = this.store;
27813         this.footer.container = fctr;
27814         this.footer = Roo.factory(this.footer, Roo);
27815         fctr.insertFirst(this.el);
27816         
27817         // this is a bit insane - as the paging toolbar seems to detach the el..
27818 //        dom.parentNode.parentNode.parentNode
27819          // they get detached?
27820     }
27821     
27822     
27823     Roo.View.superclass.constructor.call(this);
27824     
27825     
27826 };
27827
27828 Roo.extend(Roo.View, Roo.util.Observable, {
27829     
27830      /**
27831      * @cfg {Roo.data.Store} store Data store to load data from.
27832      */
27833     store : false,
27834     
27835     /**
27836      * @cfg {String|Roo.Element} el The container element.
27837      */
27838     el : '',
27839     
27840     /**
27841      * @cfg {String|Roo.Template} tpl The template used by this View 
27842      */
27843     tpl : false,
27844     /**
27845      * @cfg {String} dataName the named area of the template to use as the data area
27846      *                          Works with domtemplates roo-name="name"
27847      */
27848     dataName: false,
27849     /**
27850      * @cfg {String} selectedClass The css class to add to selected nodes
27851      */
27852     selectedClass : "x-view-selected",
27853      /**
27854      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27855      */
27856     emptyText : "",
27857     
27858     /**
27859      * @cfg {String} text to display on mask (default Loading)
27860      */
27861     mask : false,
27862     /**
27863      * @cfg {Boolean} multiSelect Allow multiple selection
27864      */
27865     multiSelect : false,
27866     /**
27867      * @cfg {Boolean} singleSelect Allow single selection
27868      */
27869     singleSelect:  false,
27870     
27871     /**
27872      * @cfg {Boolean} toggleSelect - selecting 
27873      */
27874     toggleSelect : false,
27875     
27876     /**
27877      * @cfg {Boolean} tickable - selecting 
27878      */
27879     tickable : false,
27880     
27881     /**
27882      * Returns the element this view is bound to.
27883      * @return {Roo.Element}
27884      */
27885     getEl : function(){
27886         return this.wrapEl;
27887     },
27888     
27889     
27890
27891     /**
27892      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27893      */
27894     refresh : function(){
27895         //Roo.log('refresh');
27896         var t = this.tpl;
27897         
27898         // if we are using something like 'domtemplate', then
27899         // the what gets used is:
27900         // t.applySubtemplate(NAME, data, wrapping data..)
27901         // the outer template then get' applied with
27902         //     the store 'extra data'
27903         // and the body get's added to the
27904         //      roo-name="data" node?
27905         //      <span class='roo-tpl-{name}'></span> ?????
27906         
27907         
27908         
27909         this.clearSelections();
27910         this.el.update("");
27911         var html = [];
27912         var records = this.store.getRange();
27913         if(records.length < 1) {
27914             
27915             // is this valid??  = should it render a template??
27916             
27917             this.el.update(this.emptyText);
27918             return;
27919         }
27920         var el = this.el;
27921         if (this.dataName) {
27922             this.el.update(t.apply(this.store.meta)); //????
27923             el = this.el.child('.roo-tpl-' + this.dataName);
27924         }
27925         
27926         for(var i = 0, len = records.length; i < len; i++){
27927             var data = this.prepareData(records[i].data, i, records[i]);
27928             this.fireEvent("preparedata", this, data, i, records[i]);
27929             
27930             var d = Roo.apply({}, data);
27931             
27932             if(this.tickable){
27933                 Roo.apply(d, {'roo-id' : Roo.id()});
27934                 
27935                 var _this = this;
27936             
27937                 Roo.each(this.parent.item, function(item){
27938                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27939                         return;
27940                     }
27941                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27942                 });
27943             }
27944             
27945             html[html.length] = Roo.util.Format.trim(
27946                 this.dataName ?
27947                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27948                     t.apply(d)
27949             );
27950         }
27951         
27952         
27953         
27954         el.update(html.join(""));
27955         this.nodes = el.dom.childNodes;
27956         this.updateIndexes(0);
27957     },
27958     
27959
27960     /**
27961      * Function to override to reformat the data that is sent to
27962      * the template for each node.
27963      * DEPRICATED - use the preparedata event handler.
27964      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27965      * a JSON object for an UpdateManager bound view).
27966      */
27967     prepareData : function(data, index, record)
27968     {
27969         this.fireEvent("preparedata", this, data, index, record);
27970         return data;
27971     },
27972
27973     onUpdate : function(ds, record){
27974         // Roo.log('on update');   
27975         this.clearSelections();
27976         var index = this.store.indexOf(record);
27977         var n = this.nodes[index];
27978         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27979         n.parentNode.removeChild(n);
27980         this.updateIndexes(index, index);
27981     },
27982
27983     
27984     
27985 // --------- FIXME     
27986     onAdd : function(ds, records, index)
27987     {
27988         //Roo.log(['on Add', ds, records, index] );        
27989         this.clearSelections();
27990         if(this.nodes.length == 0){
27991             this.refresh();
27992             return;
27993         }
27994         var n = this.nodes[index];
27995         for(var i = 0, len = records.length; i < len; i++){
27996             var d = this.prepareData(records[i].data, i, records[i]);
27997             if(n){
27998                 this.tpl.insertBefore(n, d);
27999             }else{
28000                 
28001                 this.tpl.append(this.el, d);
28002             }
28003         }
28004         this.updateIndexes(index);
28005     },
28006
28007     onRemove : function(ds, record, index){
28008        // Roo.log('onRemove');
28009         this.clearSelections();
28010         var el = this.dataName  ?
28011             this.el.child('.roo-tpl-' + this.dataName) :
28012             this.el; 
28013         
28014         el.dom.removeChild(this.nodes[index]);
28015         this.updateIndexes(index);
28016     },
28017
28018     /**
28019      * Refresh an individual node.
28020      * @param {Number} index
28021      */
28022     refreshNode : function(index){
28023         this.onUpdate(this.store, this.store.getAt(index));
28024     },
28025
28026     updateIndexes : function(startIndex, endIndex){
28027         var ns = this.nodes;
28028         startIndex = startIndex || 0;
28029         endIndex = endIndex || ns.length - 1;
28030         for(var i = startIndex; i <= endIndex; i++){
28031             ns[i].nodeIndex = i;
28032         }
28033     },
28034
28035     /**
28036      * Changes the data store this view uses and refresh the view.
28037      * @param {Store} store
28038      */
28039     setStore : function(store, initial){
28040         if(!initial && this.store){
28041             this.store.un("datachanged", this.refresh);
28042             this.store.un("add", this.onAdd);
28043             this.store.un("remove", this.onRemove);
28044             this.store.un("update", this.onUpdate);
28045             this.store.un("clear", this.refresh);
28046             this.store.un("beforeload", this.onBeforeLoad);
28047             this.store.un("load", this.onLoad);
28048             this.store.un("loadexception", this.onLoad);
28049         }
28050         if(store){
28051           
28052             store.on("datachanged", this.refresh, this);
28053             store.on("add", this.onAdd, this);
28054             store.on("remove", this.onRemove, this);
28055             store.on("update", this.onUpdate, this);
28056             store.on("clear", this.refresh, this);
28057             store.on("beforeload", this.onBeforeLoad, this);
28058             store.on("load", this.onLoad, this);
28059             store.on("loadexception", this.onLoad, this);
28060         }
28061         
28062         if(store){
28063             this.refresh();
28064         }
28065     },
28066     /**
28067      * onbeforeLoad - masks the loading area.
28068      *
28069      */
28070     onBeforeLoad : function(store,opts)
28071     {
28072          //Roo.log('onBeforeLoad');   
28073         if (!opts.add) {
28074             this.el.update("");
28075         }
28076         this.el.mask(this.mask ? this.mask : "Loading" ); 
28077     },
28078     onLoad : function ()
28079     {
28080         this.el.unmask();
28081     },
28082     
28083
28084     /**
28085      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28086      * @param {HTMLElement} node
28087      * @return {HTMLElement} The template node
28088      */
28089     findItemFromChild : function(node){
28090         var el = this.dataName  ?
28091             this.el.child('.roo-tpl-' + this.dataName,true) :
28092             this.el.dom; 
28093         
28094         if(!node || node.parentNode == el){
28095                     return node;
28096             }
28097             var p = node.parentNode;
28098             while(p && p != el){
28099             if(p.parentNode == el){
28100                 return p;
28101             }
28102             p = p.parentNode;
28103         }
28104             return null;
28105     },
28106
28107     /** @ignore */
28108     onClick : function(e){
28109         var item = this.findItemFromChild(e.getTarget());
28110         if(item){
28111             var index = this.indexOf(item);
28112             if(this.onItemClick(item, index, e) !== false){
28113                 this.fireEvent("click", this, index, item, e);
28114             }
28115         }else{
28116             this.clearSelections();
28117         }
28118     },
28119
28120     /** @ignore */
28121     onContextMenu : function(e){
28122         var item = this.findItemFromChild(e.getTarget());
28123         if(item){
28124             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28125         }
28126     },
28127
28128     /** @ignore */
28129     onDblClick : function(e){
28130         var item = this.findItemFromChild(e.getTarget());
28131         if(item){
28132             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28133         }
28134     },
28135
28136     onItemClick : function(item, index, e)
28137     {
28138         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28139             return false;
28140         }
28141         if (this.toggleSelect) {
28142             var m = this.isSelected(item) ? 'unselect' : 'select';
28143             //Roo.log(m);
28144             var _t = this;
28145             _t[m](item, true, false);
28146             return true;
28147         }
28148         if(this.multiSelect || this.singleSelect){
28149             if(this.multiSelect && e.shiftKey && this.lastSelection){
28150                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28151             }else{
28152                 this.select(item, this.multiSelect && e.ctrlKey);
28153                 this.lastSelection = item;
28154             }
28155             
28156             if(!this.tickable){
28157                 e.preventDefault();
28158             }
28159             
28160         }
28161         return true;
28162     },
28163
28164     /**
28165      * Get the number of selected nodes.
28166      * @return {Number}
28167      */
28168     getSelectionCount : function(){
28169         return this.selections.length;
28170     },
28171
28172     /**
28173      * Get the currently selected nodes.
28174      * @return {Array} An array of HTMLElements
28175      */
28176     getSelectedNodes : function(){
28177         return this.selections;
28178     },
28179
28180     /**
28181      * Get the indexes of the selected nodes.
28182      * @return {Array}
28183      */
28184     getSelectedIndexes : function(){
28185         var indexes = [], s = this.selections;
28186         for(var i = 0, len = s.length; i < len; i++){
28187             indexes.push(s[i].nodeIndex);
28188         }
28189         return indexes;
28190     },
28191
28192     /**
28193      * Clear all selections
28194      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28195      */
28196     clearSelections : function(suppressEvent){
28197         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28198             this.cmp.elements = this.selections;
28199             this.cmp.removeClass(this.selectedClass);
28200             this.selections = [];
28201             if(!suppressEvent){
28202                 this.fireEvent("selectionchange", this, this.selections);
28203             }
28204         }
28205     },
28206
28207     /**
28208      * Returns true if the passed node is selected
28209      * @param {HTMLElement/Number} node The node or node index
28210      * @return {Boolean}
28211      */
28212     isSelected : function(node){
28213         var s = this.selections;
28214         if(s.length < 1){
28215             return false;
28216         }
28217         node = this.getNode(node);
28218         return s.indexOf(node) !== -1;
28219     },
28220
28221     /**
28222      * Selects nodes.
28223      * @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
28224      * @param {Boolean} keepExisting (optional) true to keep existing selections
28225      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28226      */
28227     select : function(nodeInfo, keepExisting, suppressEvent){
28228         if(nodeInfo instanceof Array){
28229             if(!keepExisting){
28230                 this.clearSelections(true);
28231             }
28232             for(var i = 0, len = nodeInfo.length; i < len; i++){
28233                 this.select(nodeInfo[i], true, true);
28234             }
28235             return;
28236         } 
28237         var node = this.getNode(nodeInfo);
28238         if(!node || this.isSelected(node)){
28239             return; // already selected.
28240         }
28241         if(!keepExisting){
28242             this.clearSelections(true);
28243         }
28244         
28245         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28246             Roo.fly(node).addClass(this.selectedClass);
28247             this.selections.push(node);
28248             if(!suppressEvent){
28249                 this.fireEvent("selectionchange", this, this.selections);
28250             }
28251         }
28252         
28253         
28254     },
28255       /**
28256      * Unselects nodes.
28257      * @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
28258      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28259      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28260      */
28261     unselect : function(nodeInfo, keepExisting, suppressEvent)
28262     {
28263         if(nodeInfo instanceof Array){
28264             Roo.each(this.selections, function(s) {
28265                 this.unselect(s, nodeInfo);
28266             }, this);
28267             return;
28268         }
28269         var node = this.getNode(nodeInfo);
28270         if(!node || !this.isSelected(node)){
28271             //Roo.log("not selected");
28272             return; // not selected.
28273         }
28274         // fireevent???
28275         var ns = [];
28276         Roo.each(this.selections, function(s) {
28277             if (s == node ) {
28278                 Roo.fly(node).removeClass(this.selectedClass);
28279
28280                 return;
28281             }
28282             ns.push(s);
28283         },this);
28284         
28285         this.selections= ns;
28286         this.fireEvent("selectionchange", this, this.selections);
28287     },
28288
28289     /**
28290      * Gets a template node.
28291      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28292      * @return {HTMLElement} The node or null if it wasn't found
28293      */
28294     getNode : function(nodeInfo){
28295         if(typeof nodeInfo == "string"){
28296             return document.getElementById(nodeInfo);
28297         }else if(typeof nodeInfo == "number"){
28298             return this.nodes[nodeInfo];
28299         }
28300         return nodeInfo;
28301     },
28302
28303     /**
28304      * Gets a range template nodes.
28305      * @param {Number} startIndex
28306      * @param {Number} endIndex
28307      * @return {Array} An array of nodes
28308      */
28309     getNodes : function(start, end){
28310         var ns = this.nodes;
28311         start = start || 0;
28312         end = typeof end == "undefined" ? ns.length - 1 : end;
28313         var nodes = [];
28314         if(start <= end){
28315             for(var i = start; i <= end; i++){
28316                 nodes.push(ns[i]);
28317             }
28318         } else{
28319             for(var i = start; i >= end; i--){
28320                 nodes.push(ns[i]);
28321             }
28322         }
28323         return nodes;
28324     },
28325
28326     /**
28327      * Finds the index of the passed node
28328      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28329      * @return {Number} The index of the node or -1
28330      */
28331     indexOf : function(node){
28332         node = this.getNode(node);
28333         if(typeof node.nodeIndex == "number"){
28334             return node.nodeIndex;
28335         }
28336         var ns = this.nodes;
28337         for(var i = 0, len = ns.length; i < len; i++){
28338             if(ns[i] == node){
28339                 return i;
28340             }
28341         }
28342         return -1;
28343     }
28344 });
28345 /*
28346  * Based on:
28347  * Ext JS Library 1.1.1
28348  * Copyright(c) 2006-2007, Ext JS, LLC.
28349  *
28350  * Originally Released Under LGPL - original licence link has changed is not relivant.
28351  *
28352  * Fork - LGPL
28353  * <script type="text/javascript">
28354  */
28355
28356 /**
28357  * @class Roo.JsonView
28358  * @extends Roo.View
28359  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28360 <pre><code>
28361 var view = new Roo.JsonView({
28362     container: "my-element",
28363     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28364     multiSelect: true, 
28365     jsonRoot: "data" 
28366 });
28367
28368 // listen for node click?
28369 view.on("click", function(vw, index, node, e){
28370     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28371 });
28372
28373 // direct load of JSON data
28374 view.load("foobar.php");
28375
28376 // Example from my blog list
28377 var tpl = new Roo.Template(
28378     '&lt;div class="entry"&gt;' +
28379     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28380     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28381     "&lt;/div&gt;&lt;hr /&gt;"
28382 );
28383
28384 var moreView = new Roo.JsonView({
28385     container :  "entry-list", 
28386     template : tpl,
28387     jsonRoot: "posts"
28388 });
28389 moreView.on("beforerender", this.sortEntries, this);
28390 moreView.load({
28391     url: "/blog/get-posts.php",
28392     params: "allposts=true",
28393     text: "Loading Blog Entries..."
28394 });
28395 </code></pre>
28396
28397 * Note: old code is supported with arguments : (container, template, config)
28398
28399
28400  * @constructor
28401  * Create a new JsonView
28402  * 
28403  * @param {Object} config The config object
28404  * 
28405  */
28406 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28407     
28408     
28409     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28410
28411     var um = this.el.getUpdateManager();
28412     um.setRenderer(this);
28413     um.on("update", this.onLoad, this);
28414     um.on("failure", this.onLoadException, this);
28415
28416     /**
28417      * @event beforerender
28418      * Fires before rendering of the downloaded JSON data.
28419      * @param {Roo.JsonView} this
28420      * @param {Object} data The JSON data loaded
28421      */
28422     /**
28423      * @event load
28424      * Fires when data is loaded.
28425      * @param {Roo.JsonView} this
28426      * @param {Object} data The JSON data loaded
28427      * @param {Object} response The raw Connect response object
28428      */
28429     /**
28430      * @event loadexception
28431      * Fires when loading fails.
28432      * @param {Roo.JsonView} this
28433      * @param {Object} response The raw Connect response object
28434      */
28435     this.addEvents({
28436         'beforerender' : true,
28437         'load' : true,
28438         'loadexception' : true
28439     });
28440 };
28441 Roo.extend(Roo.JsonView, Roo.View, {
28442     /**
28443      * @type {String} The root property in the loaded JSON object that contains the data
28444      */
28445     jsonRoot : "",
28446
28447     /**
28448      * Refreshes the view.
28449      */
28450     refresh : function(){
28451         this.clearSelections();
28452         this.el.update("");
28453         var html = [];
28454         var o = this.jsonData;
28455         if(o && o.length > 0){
28456             for(var i = 0, len = o.length; i < len; i++){
28457                 var data = this.prepareData(o[i], i, o);
28458                 html[html.length] = this.tpl.apply(data);
28459             }
28460         }else{
28461             html.push(this.emptyText);
28462         }
28463         this.el.update(html.join(""));
28464         this.nodes = this.el.dom.childNodes;
28465         this.updateIndexes(0);
28466     },
28467
28468     /**
28469      * 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.
28470      * @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:
28471      <pre><code>
28472      view.load({
28473          url: "your-url.php",
28474          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28475          callback: yourFunction,
28476          scope: yourObject, //(optional scope)
28477          discardUrl: false,
28478          nocache: false,
28479          text: "Loading...",
28480          timeout: 30,
28481          scripts: false
28482      });
28483      </code></pre>
28484      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28485      * 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.
28486      * @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}
28487      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28488      * @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.
28489      */
28490     load : function(){
28491         var um = this.el.getUpdateManager();
28492         um.update.apply(um, arguments);
28493     },
28494
28495     // note - render is a standard framework call...
28496     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28497     render : function(el, response){
28498         
28499         this.clearSelections();
28500         this.el.update("");
28501         var o;
28502         try{
28503             if (response != '') {
28504                 o = Roo.util.JSON.decode(response.responseText);
28505                 if(this.jsonRoot){
28506                     
28507                     o = o[this.jsonRoot];
28508                 }
28509             }
28510         } catch(e){
28511         }
28512         /**
28513          * The current JSON data or null
28514          */
28515         this.jsonData = o;
28516         this.beforeRender();
28517         this.refresh();
28518     },
28519
28520 /**
28521  * Get the number of records in the current JSON dataset
28522  * @return {Number}
28523  */
28524     getCount : function(){
28525         return this.jsonData ? this.jsonData.length : 0;
28526     },
28527
28528 /**
28529  * Returns the JSON object for the specified node(s)
28530  * @param {HTMLElement/Array} node The node or an array of nodes
28531  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28532  * you get the JSON object for the node
28533  */
28534     getNodeData : function(node){
28535         if(node instanceof Array){
28536             var data = [];
28537             for(var i = 0, len = node.length; i < len; i++){
28538                 data.push(this.getNodeData(node[i]));
28539             }
28540             return data;
28541         }
28542         return this.jsonData[this.indexOf(node)] || null;
28543     },
28544
28545     beforeRender : function(){
28546         this.snapshot = this.jsonData;
28547         if(this.sortInfo){
28548             this.sort.apply(this, this.sortInfo);
28549         }
28550         this.fireEvent("beforerender", this, this.jsonData);
28551     },
28552
28553     onLoad : function(el, o){
28554         this.fireEvent("load", this, this.jsonData, o);
28555     },
28556
28557     onLoadException : function(el, o){
28558         this.fireEvent("loadexception", this, o);
28559     },
28560
28561 /**
28562  * Filter the data by a specific property.
28563  * @param {String} property A property on your JSON objects
28564  * @param {String/RegExp} value Either string that the property values
28565  * should start with, or a RegExp to test against the property
28566  */
28567     filter : function(property, value){
28568         if(this.jsonData){
28569             var data = [];
28570             var ss = this.snapshot;
28571             if(typeof value == "string"){
28572                 var vlen = value.length;
28573                 if(vlen == 0){
28574                     this.clearFilter();
28575                     return;
28576                 }
28577                 value = value.toLowerCase();
28578                 for(var i = 0, len = ss.length; i < len; i++){
28579                     var o = ss[i];
28580                     if(o[property].substr(0, vlen).toLowerCase() == value){
28581                         data.push(o);
28582                     }
28583                 }
28584             } else if(value.exec){ // regex?
28585                 for(var i = 0, len = ss.length; i < len; i++){
28586                     var o = ss[i];
28587                     if(value.test(o[property])){
28588                         data.push(o);
28589                     }
28590                 }
28591             } else{
28592                 return;
28593             }
28594             this.jsonData = data;
28595             this.refresh();
28596         }
28597     },
28598
28599 /**
28600  * Filter by a function. The passed function will be called with each
28601  * object in the current dataset. If the function returns true the value is kept,
28602  * otherwise it is filtered.
28603  * @param {Function} fn
28604  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28605  */
28606     filterBy : function(fn, scope){
28607         if(this.jsonData){
28608             var data = [];
28609             var ss = this.snapshot;
28610             for(var i = 0, len = ss.length; i < len; i++){
28611                 var o = ss[i];
28612                 if(fn.call(scope || this, o)){
28613                     data.push(o);
28614                 }
28615             }
28616             this.jsonData = data;
28617             this.refresh();
28618         }
28619     },
28620
28621 /**
28622  * Clears the current filter.
28623  */
28624     clearFilter : function(){
28625         if(this.snapshot && this.jsonData != this.snapshot){
28626             this.jsonData = this.snapshot;
28627             this.refresh();
28628         }
28629     },
28630
28631
28632 /**
28633  * Sorts the data for this view and refreshes it.
28634  * @param {String} property A property on your JSON objects to sort on
28635  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28636  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28637  */
28638     sort : function(property, dir, sortType){
28639         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28640         if(this.jsonData){
28641             var p = property;
28642             var dsc = dir && dir.toLowerCase() == "desc";
28643             var f = function(o1, o2){
28644                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28645                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28646                 ;
28647                 if(v1 < v2){
28648                     return dsc ? +1 : -1;
28649                 } else if(v1 > v2){
28650                     return dsc ? -1 : +1;
28651                 } else{
28652                     return 0;
28653                 }
28654             };
28655             this.jsonData.sort(f);
28656             this.refresh();
28657             if(this.jsonData != this.snapshot){
28658                 this.snapshot.sort(f);
28659             }
28660         }
28661     }
28662 });/*
28663  * Based on:
28664  * Ext JS Library 1.1.1
28665  * Copyright(c) 2006-2007, Ext JS, LLC.
28666  *
28667  * Originally Released Under LGPL - original licence link has changed is not relivant.
28668  *
28669  * Fork - LGPL
28670  * <script type="text/javascript">
28671  */
28672  
28673
28674 /**
28675  * @class Roo.ColorPalette
28676  * @extends Roo.Component
28677  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28678  * Here's an example of typical usage:
28679  * <pre><code>
28680 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28681 cp.render('my-div');
28682
28683 cp.on('select', function(palette, selColor){
28684     // do something with selColor
28685 });
28686 </code></pre>
28687  * @constructor
28688  * Create a new ColorPalette
28689  * @param {Object} config The config object
28690  */
28691 Roo.ColorPalette = function(config){
28692     Roo.ColorPalette.superclass.constructor.call(this, config);
28693     this.addEvents({
28694         /**
28695              * @event select
28696              * Fires when a color is selected
28697              * @param {ColorPalette} this
28698              * @param {String} color The 6-digit color hex code (without the # symbol)
28699              */
28700         select: true
28701     });
28702
28703     if(this.handler){
28704         this.on("select", this.handler, this.scope, true);
28705     }
28706 };
28707 Roo.extend(Roo.ColorPalette, Roo.Component, {
28708     /**
28709      * @cfg {String} itemCls
28710      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28711      */
28712     itemCls : "x-color-palette",
28713     /**
28714      * @cfg {String} value
28715      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28716      * the hex codes are case-sensitive.
28717      */
28718     value : null,
28719     clickEvent:'click',
28720     // private
28721     ctype: "Roo.ColorPalette",
28722
28723     /**
28724      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28725      */
28726     allowReselect : false,
28727
28728     /**
28729      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28730      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28731      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28732      * of colors with the width setting until the box is symmetrical.</p>
28733      * <p>You can override individual colors if needed:</p>
28734      * <pre><code>
28735 var cp = new Roo.ColorPalette();
28736 cp.colors[0] = "FF0000";  // change the first box to red
28737 </code></pre>
28738
28739 Or you can provide a custom array of your own for complete control:
28740 <pre><code>
28741 var cp = new Roo.ColorPalette();
28742 cp.colors = ["000000", "993300", "333300"];
28743 </code></pre>
28744      * @type Array
28745      */
28746     colors : [
28747         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28748         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28749         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28750         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28751         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28752     ],
28753
28754     // private
28755     onRender : function(container, position){
28756         var t = new Roo.MasterTemplate(
28757             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28758         );
28759         var c = this.colors;
28760         for(var i = 0, len = c.length; i < len; i++){
28761             t.add([c[i]]);
28762         }
28763         var el = document.createElement("div");
28764         el.className = this.itemCls;
28765         t.overwrite(el);
28766         container.dom.insertBefore(el, position);
28767         this.el = Roo.get(el);
28768         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28769         if(this.clickEvent != 'click'){
28770             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28771         }
28772     },
28773
28774     // private
28775     afterRender : function(){
28776         Roo.ColorPalette.superclass.afterRender.call(this);
28777         if(this.value){
28778             var s = this.value;
28779             this.value = null;
28780             this.select(s);
28781         }
28782     },
28783
28784     // private
28785     handleClick : function(e, t){
28786         e.preventDefault();
28787         if(!this.disabled){
28788             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28789             this.select(c.toUpperCase());
28790         }
28791     },
28792
28793     /**
28794      * Selects the specified color in the palette (fires the select event)
28795      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28796      */
28797     select : function(color){
28798         color = color.replace("#", "");
28799         if(color != this.value || this.allowReselect){
28800             var el = this.el;
28801             if(this.value){
28802                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28803             }
28804             el.child("a.color-"+color).addClass("x-color-palette-sel");
28805             this.value = color;
28806             this.fireEvent("select", this, color);
28807         }
28808     }
28809 });/*
28810  * Based on:
28811  * Ext JS Library 1.1.1
28812  * Copyright(c) 2006-2007, Ext JS, LLC.
28813  *
28814  * Originally Released Under LGPL - original licence link has changed is not relivant.
28815  *
28816  * Fork - LGPL
28817  * <script type="text/javascript">
28818  */
28819  
28820 /**
28821  * @class Roo.DatePicker
28822  * @extends Roo.Component
28823  * Simple date picker class.
28824  * @constructor
28825  * Create a new DatePicker
28826  * @param {Object} config The config object
28827  */
28828 Roo.DatePicker = function(config){
28829     Roo.DatePicker.superclass.constructor.call(this, config);
28830
28831     this.value = config && config.value ?
28832                  config.value.clearTime() : new Date().clearTime();
28833
28834     this.addEvents({
28835         /**
28836              * @event select
28837              * Fires when a date is selected
28838              * @param {DatePicker} this
28839              * @param {Date} date The selected date
28840              */
28841         'select': true,
28842         /**
28843              * @event monthchange
28844              * Fires when the displayed month changes 
28845              * @param {DatePicker} this
28846              * @param {Date} date The selected month
28847              */
28848         'monthchange': true
28849     });
28850
28851     if(this.handler){
28852         this.on("select", this.handler,  this.scope || this);
28853     }
28854     // build the disabledDatesRE
28855     if(!this.disabledDatesRE && this.disabledDates){
28856         var dd = this.disabledDates;
28857         var re = "(?:";
28858         for(var i = 0; i < dd.length; i++){
28859             re += dd[i];
28860             if(i != dd.length-1) {
28861                 re += "|";
28862             }
28863         }
28864         this.disabledDatesRE = new RegExp(re + ")");
28865     }
28866 };
28867
28868 Roo.extend(Roo.DatePicker, Roo.Component, {
28869     /**
28870      * @cfg {String} todayText
28871      * The text to display on the button that selects the current date (defaults to "Today")
28872      */
28873     todayText : "Today",
28874     /**
28875      * @cfg {String} okText
28876      * The text to display on the ok button
28877      */
28878     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28879     /**
28880      * @cfg {String} cancelText
28881      * The text to display on the cancel button
28882      */
28883     cancelText : "Cancel",
28884     /**
28885      * @cfg {String} todayTip
28886      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28887      */
28888     todayTip : "{0} (Spacebar)",
28889     /**
28890      * @cfg {Date} minDate
28891      * Minimum allowable date (JavaScript date object, defaults to null)
28892      */
28893     minDate : null,
28894     /**
28895      * @cfg {Date} maxDate
28896      * Maximum allowable date (JavaScript date object, defaults to null)
28897      */
28898     maxDate : null,
28899     /**
28900      * @cfg {String} minText
28901      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28902      */
28903     minText : "This date is before the minimum date",
28904     /**
28905      * @cfg {String} maxText
28906      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28907      */
28908     maxText : "This date is after the maximum date",
28909     /**
28910      * @cfg {String} format
28911      * The default date format string which can be overriden for localization support.  The format must be
28912      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28913      */
28914     format : "m/d/y",
28915     /**
28916      * @cfg {Array} disabledDays
28917      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28918      */
28919     disabledDays : null,
28920     /**
28921      * @cfg {String} disabledDaysText
28922      * The tooltip to display when the date falls on a disabled day (defaults to "")
28923      */
28924     disabledDaysText : "",
28925     /**
28926      * @cfg {RegExp} disabledDatesRE
28927      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28928      */
28929     disabledDatesRE : null,
28930     /**
28931      * @cfg {String} disabledDatesText
28932      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28933      */
28934     disabledDatesText : "",
28935     /**
28936      * @cfg {Boolean} constrainToViewport
28937      * True to constrain the date picker to the viewport (defaults to true)
28938      */
28939     constrainToViewport : true,
28940     /**
28941      * @cfg {Array} monthNames
28942      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28943      */
28944     monthNames : Date.monthNames,
28945     /**
28946      * @cfg {Array} dayNames
28947      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28948      */
28949     dayNames : Date.dayNames,
28950     /**
28951      * @cfg {String} nextText
28952      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28953      */
28954     nextText: 'Next Month (Control+Right)',
28955     /**
28956      * @cfg {String} prevText
28957      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28958      */
28959     prevText: 'Previous Month (Control+Left)',
28960     /**
28961      * @cfg {String} monthYearText
28962      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28963      */
28964     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28965     /**
28966      * @cfg {Number} startDay
28967      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28968      */
28969     startDay : 0,
28970     /**
28971      * @cfg {Bool} showClear
28972      * Show a clear button (usefull for date form elements that can be blank.)
28973      */
28974     
28975     showClear: false,
28976     
28977     /**
28978      * Sets the value of the date field
28979      * @param {Date} value The date to set
28980      */
28981     setValue : function(value){
28982         var old = this.value;
28983         
28984         if (typeof(value) == 'string') {
28985          
28986             value = Date.parseDate(value, this.format);
28987         }
28988         if (!value) {
28989             value = new Date();
28990         }
28991         
28992         this.value = value.clearTime(true);
28993         if(this.el){
28994             this.update(this.value);
28995         }
28996     },
28997
28998     /**
28999      * Gets the current selected value of the date field
29000      * @return {Date} The selected date
29001      */
29002     getValue : function(){
29003         return this.value;
29004     },
29005
29006     // private
29007     focus : function(){
29008         if(this.el){
29009             this.update(this.activeDate);
29010         }
29011     },
29012
29013     // privateval
29014     onRender : function(container, position){
29015         
29016         var m = [
29017              '<table cellspacing="0">',
29018                 '<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>',
29019                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29020         var dn = this.dayNames;
29021         for(var i = 0; i < 7; i++){
29022             var d = this.startDay+i;
29023             if(d > 6){
29024                 d = d-7;
29025             }
29026             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29027         }
29028         m[m.length] = "</tr></thead><tbody><tr>";
29029         for(var i = 0; i < 42; i++) {
29030             if(i % 7 == 0 && i != 0){
29031                 m[m.length] = "</tr><tr>";
29032             }
29033             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29034         }
29035         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29036             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29037
29038         var el = document.createElement("div");
29039         el.className = "x-date-picker";
29040         el.innerHTML = m.join("");
29041
29042         container.dom.insertBefore(el, position);
29043
29044         this.el = Roo.get(el);
29045         this.eventEl = Roo.get(el.firstChild);
29046
29047         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29048             handler: this.showPrevMonth,
29049             scope: this,
29050             preventDefault:true,
29051             stopDefault:true
29052         });
29053
29054         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29055             handler: this.showNextMonth,
29056             scope: this,
29057             preventDefault:true,
29058             stopDefault:true
29059         });
29060
29061         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29062
29063         this.monthPicker = this.el.down('div.x-date-mp');
29064         this.monthPicker.enableDisplayMode('block');
29065         
29066         var kn = new Roo.KeyNav(this.eventEl, {
29067             "left" : function(e){
29068                 e.ctrlKey ?
29069                     this.showPrevMonth() :
29070                     this.update(this.activeDate.add("d", -1));
29071             },
29072
29073             "right" : function(e){
29074                 e.ctrlKey ?
29075                     this.showNextMonth() :
29076                     this.update(this.activeDate.add("d", 1));
29077             },
29078
29079             "up" : function(e){
29080                 e.ctrlKey ?
29081                     this.showNextYear() :
29082                     this.update(this.activeDate.add("d", -7));
29083             },
29084
29085             "down" : function(e){
29086                 e.ctrlKey ?
29087                     this.showPrevYear() :
29088                     this.update(this.activeDate.add("d", 7));
29089             },
29090
29091             "pageUp" : function(e){
29092                 this.showNextMonth();
29093             },
29094
29095             "pageDown" : function(e){
29096                 this.showPrevMonth();
29097             },
29098
29099             "enter" : function(e){
29100                 e.stopPropagation();
29101                 return true;
29102             },
29103
29104             scope : this
29105         });
29106
29107         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29108
29109         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29110
29111         this.el.unselectable();
29112         
29113         this.cells = this.el.select("table.x-date-inner tbody td");
29114         this.textNodes = this.el.query("table.x-date-inner tbody span");
29115
29116         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29117             text: "&#160;",
29118             tooltip: this.monthYearText
29119         });
29120
29121         this.mbtn.on('click', this.showMonthPicker, this);
29122         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29123
29124
29125         var today = (new Date()).dateFormat(this.format);
29126         
29127         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29128         if (this.showClear) {
29129             baseTb.add( new Roo.Toolbar.Fill());
29130         }
29131         baseTb.add({
29132             text: String.format(this.todayText, today),
29133             tooltip: String.format(this.todayTip, today),
29134             handler: this.selectToday,
29135             scope: this
29136         });
29137         
29138         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29139             
29140         //});
29141         if (this.showClear) {
29142             
29143             baseTb.add( new Roo.Toolbar.Fill());
29144             baseTb.add({
29145                 text: '&#160;',
29146                 cls: 'x-btn-icon x-btn-clear',
29147                 handler: function() {
29148                     //this.value = '';
29149                     this.fireEvent("select", this, '');
29150                 },
29151                 scope: this
29152             });
29153         }
29154         
29155         
29156         if(Roo.isIE){
29157             this.el.repaint();
29158         }
29159         this.update(this.value);
29160     },
29161
29162     createMonthPicker : function(){
29163         if(!this.monthPicker.dom.firstChild){
29164             var buf = ['<table border="0" cellspacing="0">'];
29165             for(var i = 0; i < 6; i++){
29166                 buf.push(
29167                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29168                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29169                     i == 0 ?
29170                     '<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>' :
29171                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29172                 );
29173             }
29174             buf.push(
29175                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29176                     this.okText,
29177                     '</button><button type="button" class="x-date-mp-cancel">',
29178                     this.cancelText,
29179                     '</button></td></tr>',
29180                 '</table>'
29181             );
29182             this.monthPicker.update(buf.join(''));
29183             this.monthPicker.on('click', this.onMonthClick, this);
29184             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29185
29186             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29187             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29188
29189             this.mpMonths.each(function(m, a, i){
29190                 i += 1;
29191                 if((i%2) == 0){
29192                     m.dom.xmonth = 5 + Math.round(i * .5);
29193                 }else{
29194                     m.dom.xmonth = Math.round((i-1) * .5);
29195                 }
29196             });
29197         }
29198     },
29199
29200     showMonthPicker : function(){
29201         this.createMonthPicker();
29202         var size = this.el.getSize();
29203         this.monthPicker.setSize(size);
29204         this.monthPicker.child('table').setSize(size);
29205
29206         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29207         this.updateMPMonth(this.mpSelMonth);
29208         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29209         this.updateMPYear(this.mpSelYear);
29210
29211         this.monthPicker.slideIn('t', {duration:.2});
29212     },
29213
29214     updateMPYear : function(y){
29215         this.mpyear = y;
29216         var ys = this.mpYears.elements;
29217         for(var i = 1; i <= 10; i++){
29218             var td = ys[i-1], y2;
29219             if((i%2) == 0){
29220                 y2 = y + Math.round(i * .5);
29221                 td.firstChild.innerHTML = y2;
29222                 td.xyear = y2;
29223             }else{
29224                 y2 = y - (5-Math.round(i * .5));
29225                 td.firstChild.innerHTML = y2;
29226                 td.xyear = y2;
29227             }
29228             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29229         }
29230     },
29231
29232     updateMPMonth : function(sm){
29233         this.mpMonths.each(function(m, a, i){
29234             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29235         });
29236     },
29237
29238     selectMPMonth: function(m){
29239         
29240     },
29241
29242     onMonthClick : function(e, t){
29243         e.stopEvent();
29244         var el = new Roo.Element(t), pn;
29245         if(el.is('button.x-date-mp-cancel')){
29246             this.hideMonthPicker();
29247         }
29248         else if(el.is('button.x-date-mp-ok')){
29249             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29250             this.hideMonthPicker();
29251         }
29252         else if(pn = el.up('td.x-date-mp-month', 2)){
29253             this.mpMonths.removeClass('x-date-mp-sel');
29254             pn.addClass('x-date-mp-sel');
29255             this.mpSelMonth = pn.dom.xmonth;
29256         }
29257         else if(pn = el.up('td.x-date-mp-year', 2)){
29258             this.mpYears.removeClass('x-date-mp-sel');
29259             pn.addClass('x-date-mp-sel');
29260             this.mpSelYear = pn.dom.xyear;
29261         }
29262         else if(el.is('a.x-date-mp-prev')){
29263             this.updateMPYear(this.mpyear-10);
29264         }
29265         else if(el.is('a.x-date-mp-next')){
29266             this.updateMPYear(this.mpyear+10);
29267         }
29268     },
29269
29270     onMonthDblClick : function(e, t){
29271         e.stopEvent();
29272         var el = new Roo.Element(t), pn;
29273         if(pn = el.up('td.x-date-mp-month', 2)){
29274             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29275             this.hideMonthPicker();
29276         }
29277         else if(pn = el.up('td.x-date-mp-year', 2)){
29278             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29279             this.hideMonthPicker();
29280         }
29281     },
29282
29283     hideMonthPicker : function(disableAnim){
29284         if(this.monthPicker){
29285             if(disableAnim === true){
29286                 this.monthPicker.hide();
29287             }else{
29288                 this.monthPicker.slideOut('t', {duration:.2});
29289             }
29290         }
29291     },
29292
29293     // private
29294     showPrevMonth : function(e){
29295         this.update(this.activeDate.add("mo", -1));
29296     },
29297
29298     // private
29299     showNextMonth : function(e){
29300         this.update(this.activeDate.add("mo", 1));
29301     },
29302
29303     // private
29304     showPrevYear : function(){
29305         this.update(this.activeDate.add("y", -1));
29306     },
29307
29308     // private
29309     showNextYear : function(){
29310         this.update(this.activeDate.add("y", 1));
29311     },
29312
29313     // private
29314     handleMouseWheel : function(e){
29315         var delta = e.getWheelDelta();
29316         if(delta > 0){
29317             this.showPrevMonth();
29318             e.stopEvent();
29319         } else if(delta < 0){
29320             this.showNextMonth();
29321             e.stopEvent();
29322         }
29323     },
29324
29325     // private
29326     handleDateClick : function(e, t){
29327         e.stopEvent();
29328         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29329             this.setValue(new Date(t.dateValue));
29330             this.fireEvent("select", this, this.value);
29331         }
29332     },
29333
29334     // private
29335     selectToday : function(){
29336         this.setValue(new Date().clearTime());
29337         this.fireEvent("select", this, this.value);
29338     },
29339
29340     // private
29341     update : function(date)
29342     {
29343         var vd = this.activeDate;
29344         this.activeDate = date;
29345         if(vd && this.el){
29346             var t = date.getTime();
29347             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29348                 this.cells.removeClass("x-date-selected");
29349                 this.cells.each(function(c){
29350                    if(c.dom.firstChild.dateValue == t){
29351                        c.addClass("x-date-selected");
29352                        setTimeout(function(){
29353                             try{c.dom.firstChild.focus();}catch(e){}
29354                        }, 50);
29355                        return false;
29356                    }
29357                 });
29358                 return;
29359             }
29360         }
29361         
29362         var days = date.getDaysInMonth();
29363         var firstOfMonth = date.getFirstDateOfMonth();
29364         var startingPos = firstOfMonth.getDay()-this.startDay;
29365
29366         if(startingPos <= this.startDay){
29367             startingPos += 7;
29368         }
29369
29370         var pm = date.add("mo", -1);
29371         var prevStart = pm.getDaysInMonth()-startingPos;
29372
29373         var cells = this.cells.elements;
29374         var textEls = this.textNodes;
29375         days += startingPos;
29376
29377         // convert everything to numbers so it's fast
29378         var day = 86400000;
29379         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29380         var today = new Date().clearTime().getTime();
29381         var sel = date.clearTime().getTime();
29382         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29383         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29384         var ddMatch = this.disabledDatesRE;
29385         var ddText = this.disabledDatesText;
29386         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29387         var ddaysText = this.disabledDaysText;
29388         var format = this.format;
29389
29390         var setCellClass = function(cal, cell){
29391             cell.title = "";
29392             var t = d.getTime();
29393             cell.firstChild.dateValue = t;
29394             if(t == today){
29395                 cell.className += " x-date-today";
29396                 cell.title = cal.todayText;
29397             }
29398             if(t == sel){
29399                 cell.className += " x-date-selected";
29400                 setTimeout(function(){
29401                     try{cell.firstChild.focus();}catch(e){}
29402                 }, 50);
29403             }
29404             // disabling
29405             if(t < min) {
29406                 cell.className = " x-date-disabled";
29407                 cell.title = cal.minText;
29408                 return;
29409             }
29410             if(t > max) {
29411                 cell.className = " x-date-disabled";
29412                 cell.title = cal.maxText;
29413                 return;
29414             }
29415             if(ddays){
29416                 if(ddays.indexOf(d.getDay()) != -1){
29417                     cell.title = ddaysText;
29418                     cell.className = " x-date-disabled";
29419                 }
29420             }
29421             if(ddMatch && format){
29422                 var fvalue = d.dateFormat(format);
29423                 if(ddMatch.test(fvalue)){
29424                     cell.title = ddText.replace("%0", fvalue);
29425                     cell.className = " x-date-disabled";
29426                 }
29427             }
29428         };
29429
29430         var i = 0;
29431         for(; i < startingPos; i++) {
29432             textEls[i].innerHTML = (++prevStart);
29433             d.setDate(d.getDate()+1);
29434             cells[i].className = "x-date-prevday";
29435             setCellClass(this, cells[i]);
29436         }
29437         for(; i < days; i++){
29438             intDay = i - startingPos + 1;
29439             textEls[i].innerHTML = (intDay);
29440             d.setDate(d.getDate()+1);
29441             cells[i].className = "x-date-active";
29442             setCellClass(this, cells[i]);
29443         }
29444         var extraDays = 0;
29445         for(; i < 42; i++) {
29446              textEls[i].innerHTML = (++extraDays);
29447              d.setDate(d.getDate()+1);
29448              cells[i].className = "x-date-nextday";
29449              setCellClass(this, cells[i]);
29450         }
29451
29452         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29453         this.fireEvent('monthchange', this, date);
29454         
29455         if(!this.internalRender){
29456             var main = this.el.dom.firstChild;
29457             var w = main.offsetWidth;
29458             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29459             Roo.fly(main).setWidth(w);
29460             this.internalRender = true;
29461             // opera does not respect the auto grow header center column
29462             // then, after it gets a width opera refuses to recalculate
29463             // without a second pass
29464             if(Roo.isOpera && !this.secondPass){
29465                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29466                 this.secondPass = true;
29467                 this.update.defer(10, this, [date]);
29468             }
29469         }
29470         
29471         
29472     }
29473 });        /*
29474  * Based on:
29475  * Ext JS Library 1.1.1
29476  * Copyright(c) 2006-2007, Ext JS, LLC.
29477  *
29478  * Originally Released Under LGPL - original licence link has changed is not relivant.
29479  *
29480  * Fork - LGPL
29481  * <script type="text/javascript">
29482  */
29483 /**
29484  * @class Roo.TabPanel
29485  * @extends Roo.util.Observable
29486  * A lightweight tab container.
29487  * <br><br>
29488  * Usage:
29489  * <pre><code>
29490 // basic tabs 1, built from existing content
29491 var tabs = new Roo.TabPanel("tabs1");
29492 tabs.addTab("script", "View Script");
29493 tabs.addTab("markup", "View Markup");
29494 tabs.activate("script");
29495
29496 // more advanced tabs, built from javascript
29497 var jtabs = new Roo.TabPanel("jtabs");
29498 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29499
29500 // set up the UpdateManager
29501 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29502 var updater = tab2.getUpdateManager();
29503 updater.setDefaultUrl("ajax1.htm");
29504 tab2.on('activate', updater.refresh, updater, true);
29505
29506 // Use setUrl for Ajax loading
29507 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29508 tab3.setUrl("ajax2.htm", null, true);
29509
29510 // Disabled tab
29511 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29512 tab4.disable();
29513
29514 jtabs.activate("jtabs-1");
29515  * </code></pre>
29516  * @constructor
29517  * Create a new TabPanel.
29518  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29519  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29520  */
29521 Roo.TabPanel = function(container, config){
29522     /**
29523     * The container element for this TabPanel.
29524     * @type Roo.Element
29525     */
29526     this.el = Roo.get(container, true);
29527     if(config){
29528         if(typeof config == "boolean"){
29529             this.tabPosition = config ? "bottom" : "top";
29530         }else{
29531             Roo.apply(this, config);
29532         }
29533     }
29534     if(this.tabPosition == "bottom"){
29535         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29536         this.el.addClass("x-tabs-bottom");
29537     }
29538     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29539     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29540     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29541     if(Roo.isIE){
29542         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29543     }
29544     if(this.tabPosition != "bottom"){
29545         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29546          * @type Roo.Element
29547          */
29548         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29549         this.el.addClass("x-tabs-top");
29550     }
29551     this.items = [];
29552
29553     this.bodyEl.setStyle("position", "relative");
29554
29555     this.active = null;
29556     this.activateDelegate = this.activate.createDelegate(this);
29557
29558     this.addEvents({
29559         /**
29560          * @event tabchange
29561          * Fires when the active tab changes
29562          * @param {Roo.TabPanel} this
29563          * @param {Roo.TabPanelItem} activePanel The new active tab
29564          */
29565         "tabchange": true,
29566         /**
29567          * @event beforetabchange
29568          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29569          * @param {Roo.TabPanel} this
29570          * @param {Object} e Set cancel to true on this object to cancel the tab change
29571          * @param {Roo.TabPanelItem} tab The tab being changed to
29572          */
29573         "beforetabchange" : true
29574     });
29575
29576     Roo.EventManager.onWindowResize(this.onResize, this);
29577     this.cpad = this.el.getPadding("lr");
29578     this.hiddenCount = 0;
29579
29580
29581     // toolbar on the tabbar support...
29582     if (this.toolbar) {
29583         var tcfg = this.toolbar;
29584         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29585         this.toolbar = new Roo.Toolbar(tcfg);
29586         if (Roo.isSafari) {
29587             var tbl = tcfg.container.child('table', true);
29588             tbl.setAttribute('width', '100%');
29589         }
29590         
29591     }
29592    
29593
29594
29595     Roo.TabPanel.superclass.constructor.call(this);
29596 };
29597
29598 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29599     /*
29600      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29601      */
29602     tabPosition : "top",
29603     /*
29604      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29605      */
29606     currentTabWidth : 0,
29607     /*
29608      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29609      */
29610     minTabWidth : 40,
29611     /*
29612      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29613      */
29614     maxTabWidth : 250,
29615     /*
29616      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29617      */
29618     preferredTabWidth : 175,
29619     /*
29620      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29621      */
29622     resizeTabs : false,
29623     /*
29624      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29625      */
29626     monitorResize : true,
29627     /*
29628      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29629      */
29630     toolbar : false,
29631
29632     /**
29633      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29634      * @param {String} id The id of the div to use <b>or create</b>
29635      * @param {String} text The text for the tab
29636      * @param {String} content (optional) Content to put in the TabPanelItem body
29637      * @param {Boolean} closable (optional) True to create a close icon on the tab
29638      * @return {Roo.TabPanelItem} The created TabPanelItem
29639      */
29640     addTab : function(id, text, content, closable){
29641         var item = new Roo.TabPanelItem(this, id, text, closable);
29642         this.addTabItem(item);
29643         if(content){
29644             item.setContent(content);
29645         }
29646         return item;
29647     },
29648
29649     /**
29650      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29651      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29652      * @return {Roo.TabPanelItem}
29653      */
29654     getTab : function(id){
29655         return this.items[id];
29656     },
29657
29658     /**
29659      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29660      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29661      */
29662     hideTab : function(id){
29663         var t = this.items[id];
29664         if(!t.isHidden()){
29665            t.setHidden(true);
29666            this.hiddenCount++;
29667            this.autoSizeTabs();
29668         }
29669     },
29670
29671     /**
29672      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29673      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29674      */
29675     unhideTab : function(id){
29676         var t = this.items[id];
29677         if(t.isHidden()){
29678            t.setHidden(false);
29679            this.hiddenCount--;
29680            this.autoSizeTabs();
29681         }
29682     },
29683
29684     /**
29685      * Adds an existing {@link Roo.TabPanelItem}.
29686      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29687      */
29688     addTabItem : function(item){
29689         this.items[item.id] = item;
29690         this.items.push(item);
29691         if(this.resizeTabs){
29692            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29693            this.autoSizeTabs();
29694         }else{
29695             item.autoSize();
29696         }
29697     },
29698
29699     /**
29700      * Removes a {@link Roo.TabPanelItem}.
29701      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29702      */
29703     removeTab : function(id){
29704         var items = this.items;
29705         var tab = items[id];
29706         if(!tab) { return; }
29707         var index = items.indexOf(tab);
29708         if(this.active == tab && items.length > 1){
29709             var newTab = this.getNextAvailable(index);
29710             if(newTab) {
29711                 newTab.activate();
29712             }
29713         }
29714         this.stripEl.dom.removeChild(tab.pnode.dom);
29715         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29716             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29717         }
29718         items.splice(index, 1);
29719         delete this.items[tab.id];
29720         tab.fireEvent("close", tab);
29721         tab.purgeListeners();
29722         this.autoSizeTabs();
29723     },
29724
29725     getNextAvailable : function(start){
29726         var items = this.items;
29727         var index = start;
29728         // look for a next tab that will slide over to
29729         // replace the one being removed
29730         while(index < items.length){
29731             var item = items[++index];
29732             if(item && !item.isHidden()){
29733                 return item;
29734             }
29735         }
29736         // if one isn't found select the previous tab (on the left)
29737         index = start;
29738         while(index >= 0){
29739             var item = items[--index];
29740             if(item && !item.isHidden()){
29741                 return item;
29742             }
29743         }
29744         return null;
29745     },
29746
29747     /**
29748      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29749      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29750      */
29751     disableTab : function(id){
29752         var tab = this.items[id];
29753         if(tab && this.active != tab){
29754             tab.disable();
29755         }
29756     },
29757
29758     /**
29759      * Enables a {@link Roo.TabPanelItem} that is disabled.
29760      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29761      */
29762     enableTab : function(id){
29763         var tab = this.items[id];
29764         tab.enable();
29765     },
29766
29767     /**
29768      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29769      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29770      * @return {Roo.TabPanelItem} The TabPanelItem.
29771      */
29772     activate : function(id){
29773         var tab = this.items[id];
29774         if(!tab){
29775             return null;
29776         }
29777         if(tab == this.active || tab.disabled){
29778             return tab;
29779         }
29780         var e = {};
29781         this.fireEvent("beforetabchange", this, e, tab);
29782         if(e.cancel !== true && !tab.disabled){
29783             if(this.active){
29784                 this.active.hide();
29785             }
29786             this.active = this.items[id];
29787             this.active.show();
29788             this.fireEvent("tabchange", this, this.active);
29789         }
29790         return tab;
29791     },
29792
29793     /**
29794      * Gets the active {@link Roo.TabPanelItem}.
29795      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29796      */
29797     getActiveTab : function(){
29798         return this.active;
29799     },
29800
29801     /**
29802      * Updates the tab body element to fit the height of the container element
29803      * for overflow scrolling
29804      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29805      */
29806     syncHeight : function(targetHeight){
29807         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29808         var bm = this.bodyEl.getMargins();
29809         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29810         this.bodyEl.setHeight(newHeight);
29811         return newHeight;
29812     },
29813
29814     onResize : function(){
29815         if(this.monitorResize){
29816             this.autoSizeTabs();
29817         }
29818     },
29819
29820     /**
29821      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29822      */
29823     beginUpdate : function(){
29824         this.updating = true;
29825     },
29826
29827     /**
29828      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29829      */
29830     endUpdate : function(){
29831         this.updating = false;
29832         this.autoSizeTabs();
29833     },
29834
29835     /**
29836      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29837      */
29838     autoSizeTabs : function(){
29839         var count = this.items.length;
29840         var vcount = count - this.hiddenCount;
29841         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29842             return;
29843         }
29844         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29845         var availWidth = Math.floor(w / vcount);
29846         var b = this.stripBody;
29847         if(b.getWidth() > w){
29848             var tabs = this.items;
29849             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29850             if(availWidth < this.minTabWidth){
29851                 /*if(!this.sleft){    // incomplete scrolling code
29852                     this.createScrollButtons();
29853                 }
29854                 this.showScroll();
29855                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29856             }
29857         }else{
29858             if(this.currentTabWidth < this.preferredTabWidth){
29859                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29860             }
29861         }
29862     },
29863
29864     /**
29865      * Returns the number of tabs in this TabPanel.
29866      * @return {Number}
29867      */
29868      getCount : function(){
29869          return this.items.length;
29870      },
29871
29872     /**
29873      * Resizes all the tabs to the passed width
29874      * @param {Number} The new width
29875      */
29876     setTabWidth : function(width){
29877         this.currentTabWidth = width;
29878         for(var i = 0, len = this.items.length; i < len; i++) {
29879                 if(!this.items[i].isHidden()) {
29880                 this.items[i].setWidth(width);
29881             }
29882         }
29883     },
29884
29885     /**
29886      * Destroys this TabPanel
29887      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29888      */
29889     destroy : function(removeEl){
29890         Roo.EventManager.removeResizeListener(this.onResize, this);
29891         for(var i = 0, len = this.items.length; i < len; i++){
29892             this.items[i].purgeListeners();
29893         }
29894         if(removeEl === true){
29895             this.el.update("");
29896             this.el.remove();
29897         }
29898     }
29899 });
29900
29901 /**
29902  * @class Roo.TabPanelItem
29903  * @extends Roo.util.Observable
29904  * Represents an individual item (tab plus body) in a TabPanel.
29905  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29906  * @param {String} id The id of this TabPanelItem
29907  * @param {String} text The text for the tab of this TabPanelItem
29908  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29909  */
29910 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29911     /**
29912      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29913      * @type Roo.TabPanel
29914      */
29915     this.tabPanel = tabPanel;
29916     /**
29917      * The id for this TabPanelItem
29918      * @type String
29919      */
29920     this.id = id;
29921     /** @private */
29922     this.disabled = false;
29923     /** @private */
29924     this.text = text;
29925     /** @private */
29926     this.loaded = false;
29927     this.closable = closable;
29928
29929     /**
29930      * The body element for this TabPanelItem.
29931      * @type Roo.Element
29932      */
29933     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29934     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29935     this.bodyEl.setStyle("display", "block");
29936     this.bodyEl.setStyle("zoom", "1");
29937     this.hideAction();
29938
29939     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29940     /** @private */
29941     this.el = Roo.get(els.el, true);
29942     this.inner = Roo.get(els.inner, true);
29943     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29944     this.pnode = Roo.get(els.el.parentNode, true);
29945     this.el.on("mousedown", this.onTabMouseDown, this);
29946     this.el.on("click", this.onTabClick, this);
29947     /** @private */
29948     if(closable){
29949         var c = Roo.get(els.close, true);
29950         c.dom.title = this.closeText;
29951         c.addClassOnOver("close-over");
29952         c.on("click", this.closeClick, this);
29953      }
29954
29955     this.addEvents({
29956          /**
29957          * @event activate
29958          * Fires when this tab becomes the active tab.
29959          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29960          * @param {Roo.TabPanelItem} this
29961          */
29962         "activate": true,
29963         /**
29964          * @event beforeclose
29965          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29966          * @param {Roo.TabPanelItem} this
29967          * @param {Object} e Set cancel to true on this object to cancel the close.
29968          */
29969         "beforeclose": true,
29970         /**
29971          * @event close
29972          * Fires when this tab is closed.
29973          * @param {Roo.TabPanelItem} this
29974          */
29975          "close": true,
29976         /**
29977          * @event deactivate
29978          * Fires when this tab is no longer the active tab.
29979          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29980          * @param {Roo.TabPanelItem} this
29981          */
29982          "deactivate" : true
29983     });
29984     this.hidden = false;
29985
29986     Roo.TabPanelItem.superclass.constructor.call(this);
29987 };
29988
29989 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29990     purgeListeners : function(){
29991        Roo.util.Observable.prototype.purgeListeners.call(this);
29992        this.el.removeAllListeners();
29993     },
29994     /**
29995      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29996      */
29997     show : function(){
29998         this.pnode.addClass("on");
29999         this.showAction();
30000         if(Roo.isOpera){
30001             this.tabPanel.stripWrap.repaint();
30002         }
30003         this.fireEvent("activate", this.tabPanel, this);
30004     },
30005
30006     /**
30007      * Returns true if this tab is the active tab.
30008      * @return {Boolean}
30009      */
30010     isActive : function(){
30011         return this.tabPanel.getActiveTab() == this;
30012     },
30013
30014     /**
30015      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30016      */
30017     hide : function(){
30018         this.pnode.removeClass("on");
30019         this.hideAction();
30020         this.fireEvent("deactivate", this.tabPanel, this);
30021     },
30022
30023     hideAction : function(){
30024         this.bodyEl.hide();
30025         this.bodyEl.setStyle("position", "absolute");
30026         this.bodyEl.setLeft("-20000px");
30027         this.bodyEl.setTop("-20000px");
30028     },
30029
30030     showAction : function(){
30031         this.bodyEl.setStyle("position", "relative");
30032         this.bodyEl.setTop("");
30033         this.bodyEl.setLeft("");
30034         this.bodyEl.show();
30035     },
30036
30037     /**
30038      * Set the tooltip for the tab.
30039      * @param {String} tooltip The tab's tooltip
30040      */
30041     setTooltip : function(text){
30042         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30043             this.textEl.dom.qtip = text;
30044             this.textEl.dom.removeAttribute('title');
30045         }else{
30046             this.textEl.dom.title = text;
30047         }
30048     },
30049
30050     onTabClick : function(e){
30051         e.preventDefault();
30052         this.tabPanel.activate(this.id);
30053     },
30054
30055     onTabMouseDown : function(e){
30056         e.preventDefault();
30057         this.tabPanel.activate(this.id);
30058     },
30059
30060     getWidth : function(){
30061         return this.inner.getWidth();
30062     },
30063
30064     setWidth : function(width){
30065         var iwidth = width - this.pnode.getPadding("lr");
30066         this.inner.setWidth(iwidth);
30067         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30068         this.pnode.setWidth(width);
30069     },
30070
30071     /**
30072      * Show or hide the tab
30073      * @param {Boolean} hidden True to hide or false to show.
30074      */
30075     setHidden : function(hidden){
30076         this.hidden = hidden;
30077         this.pnode.setStyle("display", hidden ? "none" : "");
30078     },
30079
30080     /**
30081      * Returns true if this tab is "hidden"
30082      * @return {Boolean}
30083      */
30084     isHidden : function(){
30085         return this.hidden;
30086     },
30087
30088     /**
30089      * Returns the text for this tab
30090      * @return {String}
30091      */
30092     getText : function(){
30093         return this.text;
30094     },
30095
30096     autoSize : function(){
30097         //this.el.beginMeasure();
30098         this.textEl.setWidth(1);
30099         /*
30100          *  #2804 [new] Tabs in Roojs
30101          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30102          */
30103         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30104         //this.el.endMeasure();
30105     },
30106
30107     /**
30108      * Sets the text for the tab (Note: this also sets the tooltip text)
30109      * @param {String} text The tab's text and tooltip
30110      */
30111     setText : function(text){
30112         this.text = text;
30113         this.textEl.update(text);
30114         this.setTooltip(text);
30115         if(!this.tabPanel.resizeTabs){
30116             this.autoSize();
30117         }
30118     },
30119     /**
30120      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30121      */
30122     activate : function(){
30123         this.tabPanel.activate(this.id);
30124     },
30125
30126     /**
30127      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30128      */
30129     disable : function(){
30130         if(this.tabPanel.active != this){
30131             this.disabled = true;
30132             this.pnode.addClass("disabled");
30133         }
30134     },
30135
30136     /**
30137      * Enables this TabPanelItem if it was previously disabled.
30138      */
30139     enable : function(){
30140         this.disabled = false;
30141         this.pnode.removeClass("disabled");
30142     },
30143
30144     /**
30145      * Sets the content for this TabPanelItem.
30146      * @param {String} content The content
30147      * @param {Boolean} loadScripts true to look for and load scripts
30148      */
30149     setContent : function(content, loadScripts){
30150         this.bodyEl.update(content, loadScripts);
30151     },
30152
30153     /**
30154      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30155      * @return {Roo.UpdateManager} The UpdateManager
30156      */
30157     getUpdateManager : function(){
30158         return this.bodyEl.getUpdateManager();
30159     },
30160
30161     /**
30162      * Set a URL to be used to load the content for this TabPanelItem.
30163      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30164      * @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)
30165      * @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)
30166      * @return {Roo.UpdateManager} The UpdateManager
30167      */
30168     setUrl : function(url, params, loadOnce){
30169         if(this.refreshDelegate){
30170             this.un('activate', this.refreshDelegate);
30171         }
30172         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30173         this.on("activate", this.refreshDelegate);
30174         return this.bodyEl.getUpdateManager();
30175     },
30176
30177     /** @private */
30178     _handleRefresh : function(url, params, loadOnce){
30179         if(!loadOnce || !this.loaded){
30180             var updater = this.bodyEl.getUpdateManager();
30181             updater.update(url, params, this._setLoaded.createDelegate(this));
30182         }
30183     },
30184
30185     /**
30186      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30187      *   Will fail silently if the setUrl method has not been called.
30188      *   This does not activate the panel, just updates its content.
30189      */
30190     refresh : function(){
30191         if(this.refreshDelegate){
30192            this.loaded = false;
30193            this.refreshDelegate();
30194         }
30195     },
30196
30197     /** @private */
30198     _setLoaded : function(){
30199         this.loaded = true;
30200     },
30201
30202     /** @private */
30203     closeClick : function(e){
30204         var o = {};
30205         e.stopEvent();
30206         this.fireEvent("beforeclose", this, o);
30207         if(o.cancel !== true){
30208             this.tabPanel.removeTab(this.id);
30209         }
30210     },
30211     /**
30212      * The text displayed in the tooltip for the close icon.
30213      * @type String
30214      */
30215     closeText : "Close this tab"
30216 });
30217
30218 /** @private */
30219 Roo.TabPanel.prototype.createStrip = function(container){
30220     var strip = document.createElement("div");
30221     strip.className = "x-tabs-wrap";
30222     container.appendChild(strip);
30223     return strip;
30224 };
30225 /** @private */
30226 Roo.TabPanel.prototype.createStripList = function(strip){
30227     // div wrapper for retard IE
30228     // returns the "tr" element.
30229     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30230         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30231         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30232     return strip.firstChild.firstChild.firstChild.firstChild;
30233 };
30234 /** @private */
30235 Roo.TabPanel.prototype.createBody = function(container){
30236     var body = document.createElement("div");
30237     Roo.id(body, "tab-body");
30238     Roo.fly(body).addClass("x-tabs-body");
30239     container.appendChild(body);
30240     return body;
30241 };
30242 /** @private */
30243 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30244     var body = Roo.getDom(id);
30245     if(!body){
30246         body = document.createElement("div");
30247         body.id = id;
30248     }
30249     Roo.fly(body).addClass("x-tabs-item-body");
30250     bodyEl.insertBefore(body, bodyEl.firstChild);
30251     return body;
30252 };
30253 /** @private */
30254 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30255     var td = document.createElement("td");
30256     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30257     //stripEl.appendChild(td);
30258     if(closable){
30259         td.className = "x-tabs-closable";
30260         if(!this.closeTpl){
30261             this.closeTpl = new Roo.Template(
30262                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30263                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30264                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30265             );
30266         }
30267         var el = this.closeTpl.overwrite(td, {"text": text});
30268         var close = el.getElementsByTagName("div")[0];
30269         var inner = el.getElementsByTagName("em")[0];
30270         return {"el": el, "close": close, "inner": inner};
30271     } else {
30272         if(!this.tabTpl){
30273             this.tabTpl = new Roo.Template(
30274                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30275                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30276             );
30277         }
30278         var el = this.tabTpl.overwrite(td, {"text": text});
30279         var inner = el.getElementsByTagName("em")[0];
30280         return {"el": el, "inner": inner};
30281     }
30282 };/*
30283  * Based on:
30284  * Ext JS Library 1.1.1
30285  * Copyright(c) 2006-2007, Ext JS, LLC.
30286  *
30287  * Originally Released Under LGPL - original licence link has changed is not relivant.
30288  *
30289  * Fork - LGPL
30290  * <script type="text/javascript">
30291  */
30292
30293 /**
30294  * @class Roo.Button
30295  * @extends Roo.util.Observable
30296  * Simple Button class
30297  * @cfg {String} text The button text
30298  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30299  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30300  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30301  * @cfg {Object} scope The scope of the handler
30302  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30303  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30304  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30305  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30306  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30307  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30308    applies if enableToggle = true)
30309  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30310  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30311   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30312  * @constructor
30313  * Create a new button
30314  * @param {Object} config The config object
30315  */
30316 Roo.Button = function(renderTo, config)
30317 {
30318     if (!config) {
30319         config = renderTo;
30320         renderTo = config.renderTo || false;
30321     }
30322     
30323     Roo.apply(this, config);
30324     this.addEvents({
30325         /**
30326              * @event click
30327              * Fires when this button is clicked
30328              * @param {Button} this
30329              * @param {EventObject} e The click event
30330              */
30331             "click" : true,
30332         /**
30333              * @event toggle
30334              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30335              * @param {Button} this
30336              * @param {Boolean} pressed
30337              */
30338             "toggle" : true,
30339         /**
30340              * @event mouseover
30341              * Fires when the mouse hovers over the button
30342              * @param {Button} this
30343              * @param {Event} e The event object
30344              */
30345         'mouseover' : true,
30346         /**
30347              * @event mouseout
30348              * Fires when the mouse exits the button
30349              * @param {Button} this
30350              * @param {Event} e The event object
30351              */
30352         'mouseout': true,
30353          /**
30354              * @event render
30355              * Fires when the button is rendered
30356              * @param {Button} this
30357              */
30358         'render': true
30359     });
30360     if(this.menu){
30361         this.menu = Roo.menu.MenuMgr.get(this.menu);
30362     }
30363     // register listeners first!!  - so render can be captured..
30364     Roo.util.Observable.call(this);
30365     if(renderTo){
30366         this.render(renderTo);
30367     }
30368     
30369   
30370 };
30371
30372 Roo.extend(Roo.Button, Roo.util.Observable, {
30373     /**
30374      * 
30375      */
30376     
30377     /**
30378      * Read-only. True if this button is hidden
30379      * @type Boolean
30380      */
30381     hidden : false,
30382     /**
30383      * Read-only. True if this button is disabled
30384      * @type Boolean
30385      */
30386     disabled : false,
30387     /**
30388      * Read-only. True if this button is pressed (only if enableToggle = true)
30389      * @type Boolean
30390      */
30391     pressed : false,
30392
30393     /**
30394      * @cfg {Number} tabIndex 
30395      * The DOM tabIndex for this button (defaults to undefined)
30396      */
30397     tabIndex : undefined,
30398
30399     /**
30400      * @cfg {Boolean} enableToggle
30401      * True to enable pressed/not pressed toggling (defaults to false)
30402      */
30403     enableToggle: false,
30404     /**
30405      * @cfg {Roo.menu.Menu} menu
30406      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30407      */
30408     menu : undefined,
30409     /**
30410      * @cfg {String} menuAlign
30411      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30412      */
30413     menuAlign : "tl-bl?",
30414
30415     /**
30416      * @cfg {String} iconCls
30417      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30418      */
30419     iconCls : undefined,
30420     /**
30421      * @cfg {String} type
30422      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30423      */
30424     type : 'button',
30425
30426     // private
30427     menuClassTarget: 'tr',
30428
30429     /**
30430      * @cfg {String} clickEvent
30431      * The type of event to map to the button's event handler (defaults to 'click')
30432      */
30433     clickEvent : 'click',
30434
30435     /**
30436      * @cfg {Boolean} handleMouseEvents
30437      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30438      */
30439     handleMouseEvents : true,
30440
30441     /**
30442      * @cfg {String} tooltipType
30443      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30444      */
30445     tooltipType : 'qtip',
30446
30447     /**
30448      * @cfg {String} cls
30449      * A CSS class to apply to the button's main element.
30450      */
30451     
30452     /**
30453      * @cfg {Roo.Template} template (Optional)
30454      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30455      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30456      * require code modifications if required elements (e.g. a button) aren't present.
30457      */
30458
30459     // private
30460     render : function(renderTo){
30461         var btn;
30462         if(this.hideParent){
30463             this.parentEl = Roo.get(renderTo);
30464         }
30465         if(!this.dhconfig){
30466             if(!this.template){
30467                 if(!Roo.Button.buttonTemplate){
30468                     // hideous table template
30469                     Roo.Button.buttonTemplate = new Roo.Template(
30470                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30471                         '<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>',
30472                         "</tr></tbody></table>");
30473                 }
30474                 this.template = Roo.Button.buttonTemplate;
30475             }
30476             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30477             var btnEl = btn.child("button:first");
30478             btnEl.on('focus', this.onFocus, this);
30479             btnEl.on('blur', this.onBlur, this);
30480             if(this.cls){
30481                 btn.addClass(this.cls);
30482             }
30483             if(this.icon){
30484                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30485             }
30486             if(this.iconCls){
30487                 btnEl.addClass(this.iconCls);
30488                 if(!this.cls){
30489                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30490                 }
30491             }
30492             if(this.tabIndex !== undefined){
30493                 btnEl.dom.tabIndex = this.tabIndex;
30494             }
30495             if(this.tooltip){
30496                 if(typeof this.tooltip == 'object'){
30497                     Roo.QuickTips.tips(Roo.apply({
30498                           target: btnEl.id
30499                     }, this.tooltip));
30500                 } else {
30501                     btnEl.dom[this.tooltipType] = this.tooltip;
30502                 }
30503             }
30504         }else{
30505             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30506         }
30507         this.el = btn;
30508         if(this.id){
30509             this.el.dom.id = this.el.id = this.id;
30510         }
30511         if(this.menu){
30512             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30513             this.menu.on("show", this.onMenuShow, this);
30514             this.menu.on("hide", this.onMenuHide, this);
30515         }
30516         btn.addClass("x-btn");
30517         if(Roo.isIE && !Roo.isIE7){
30518             this.autoWidth.defer(1, this);
30519         }else{
30520             this.autoWidth();
30521         }
30522         if(this.handleMouseEvents){
30523             btn.on("mouseover", this.onMouseOver, this);
30524             btn.on("mouseout", this.onMouseOut, this);
30525             btn.on("mousedown", this.onMouseDown, this);
30526         }
30527         btn.on(this.clickEvent, this.onClick, this);
30528         //btn.on("mouseup", this.onMouseUp, this);
30529         if(this.hidden){
30530             this.hide();
30531         }
30532         if(this.disabled){
30533             this.disable();
30534         }
30535         Roo.ButtonToggleMgr.register(this);
30536         if(this.pressed){
30537             this.el.addClass("x-btn-pressed");
30538         }
30539         if(this.repeat){
30540             var repeater = new Roo.util.ClickRepeater(btn,
30541                 typeof this.repeat == "object" ? this.repeat : {}
30542             );
30543             repeater.on("click", this.onClick,  this);
30544         }
30545         
30546         this.fireEvent('render', this);
30547         
30548     },
30549     /**
30550      * Returns the button's underlying element
30551      * @return {Roo.Element} The element
30552      */
30553     getEl : function(){
30554         return this.el;  
30555     },
30556     
30557     /**
30558      * Destroys this Button and removes any listeners.
30559      */
30560     destroy : function(){
30561         Roo.ButtonToggleMgr.unregister(this);
30562         this.el.removeAllListeners();
30563         this.purgeListeners();
30564         this.el.remove();
30565     },
30566
30567     // private
30568     autoWidth : function(){
30569         if(this.el){
30570             this.el.setWidth("auto");
30571             if(Roo.isIE7 && Roo.isStrict){
30572                 var ib = this.el.child('button');
30573                 if(ib && ib.getWidth() > 20){
30574                     ib.clip();
30575                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30576                 }
30577             }
30578             if(this.minWidth){
30579                 if(this.hidden){
30580                     this.el.beginMeasure();
30581                 }
30582                 if(this.el.getWidth() < this.minWidth){
30583                     this.el.setWidth(this.minWidth);
30584                 }
30585                 if(this.hidden){
30586                     this.el.endMeasure();
30587                 }
30588             }
30589         }
30590     },
30591
30592     /**
30593      * Assigns this button's click handler
30594      * @param {Function} handler The function to call when the button is clicked
30595      * @param {Object} scope (optional) Scope for the function passed in
30596      */
30597     setHandler : function(handler, scope){
30598         this.handler = handler;
30599         this.scope = scope;  
30600     },
30601     
30602     /**
30603      * Sets this button's text
30604      * @param {String} text The button text
30605      */
30606     setText : function(text){
30607         this.text = text;
30608         if(this.el){
30609             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30610         }
30611         this.autoWidth();
30612     },
30613     
30614     /**
30615      * Gets the text for this button
30616      * @return {String} The button text
30617      */
30618     getText : function(){
30619         return this.text;  
30620     },
30621     
30622     /**
30623      * Show this button
30624      */
30625     show: function(){
30626         this.hidden = false;
30627         if(this.el){
30628             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30629         }
30630     },
30631     
30632     /**
30633      * Hide this button
30634      */
30635     hide: function(){
30636         this.hidden = true;
30637         if(this.el){
30638             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30639         }
30640     },
30641     
30642     /**
30643      * Convenience function for boolean show/hide
30644      * @param {Boolean} visible True to show, false to hide
30645      */
30646     setVisible: function(visible){
30647         if(visible) {
30648             this.show();
30649         }else{
30650             this.hide();
30651         }
30652     },
30653     
30654     /**
30655      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30656      * @param {Boolean} state (optional) Force a particular state
30657      */
30658     toggle : function(state){
30659         state = state === undefined ? !this.pressed : state;
30660         if(state != this.pressed){
30661             if(state){
30662                 this.el.addClass("x-btn-pressed");
30663                 this.pressed = true;
30664                 this.fireEvent("toggle", this, true);
30665             }else{
30666                 this.el.removeClass("x-btn-pressed");
30667                 this.pressed = false;
30668                 this.fireEvent("toggle", this, false);
30669             }
30670             if(this.toggleHandler){
30671                 this.toggleHandler.call(this.scope || this, this, state);
30672             }
30673         }
30674     },
30675     
30676     /**
30677      * Focus the button
30678      */
30679     focus : function(){
30680         this.el.child('button:first').focus();
30681     },
30682     
30683     /**
30684      * Disable this button
30685      */
30686     disable : function(){
30687         if(this.el){
30688             this.el.addClass("x-btn-disabled");
30689         }
30690         this.disabled = true;
30691     },
30692     
30693     /**
30694      * Enable this button
30695      */
30696     enable : function(){
30697         if(this.el){
30698             this.el.removeClass("x-btn-disabled");
30699         }
30700         this.disabled = false;
30701     },
30702
30703     /**
30704      * Convenience function for boolean enable/disable
30705      * @param {Boolean} enabled True to enable, false to disable
30706      */
30707     setDisabled : function(v){
30708         this[v !== true ? "enable" : "disable"]();
30709     },
30710
30711     // private
30712     onClick : function(e)
30713     {
30714         if(e){
30715             e.preventDefault();
30716         }
30717         if(e.button != 0){
30718             return;
30719         }
30720         if(!this.disabled){
30721             if(this.enableToggle){
30722                 this.toggle();
30723             }
30724             if(this.menu && !this.menu.isVisible()){
30725                 this.menu.show(this.el, this.menuAlign);
30726             }
30727             this.fireEvent("click", this, e);
30728             if(this.handler){
30729                 this.el.removeClass("x-btn-over");
30730                 this.handler.call(this.scope || this, this, e);
30731             }
30732         }
30733     },
30734     // private
30735     onMouseOver : function(e){
30736         if(!this.disabled){
30737             this.el.addClass("x-btn-over");
30738             this.fireEvent('mouseover', this, e);
30739         }
30740     },
30741     // private
30742     onMouseOut : function(e){
30743         if(!e.within(this.el,  true)){
30744             this.el.removeClass("x-btn-over");
30745             this.fireEvent('mouseout', this, e);
30746         }
30747     },
30748     // private
30749     onFocus : function(e){
30750         if(!this.disabled){
30751             this.el.addClass("x-btn-focus");
30752         }
30753     },
30754     // private
30755     onBlur : function(e){
30756         this.el.removeClass("x-btn-focus");
30757     },
30758     // private
30759     onMouseDown : function(e){
30760         if(!this.disabled && e.button == 0){
30761             this.el.addClass("x-btn-click");
30762             Roo.get(document).on('mouseup', this.onMouseUp, this);
30763         }
30764     },
30765     // private
30766     onMouseUp : function(e){
30767         if(e.button == 0){
30768             this.el.removeClass("x-btn-click");
30769             Roo.get(document).un('mouseup', this.onMouseUp, this);
30770         }
30771     },
30772     // private
30773     onMenuShow : function(e){
30774         this.el.addClass("x-btn-menu-active");
30775     },
30776     // private
30777     onMenuHide : function(e){
30778         this.el.removeClass("x-btn-menu-active");
30779     }   
30780 });
30781
30782 // Private utility class used by Button
30783 Roo.ButtonToggleMgr = function(){
30784    var groups = {};
30785    
30786    function toggleGroup(btn, state){
30787        if(state){
30788            var g = groups[btn.toggleGroup];
30789            for(var i = 0, l = g.length; i < l; i++){
30790                if(g[i] != btn){
30791                    g[i].toggle(false);
30792                }
30793            }
30794        }
30795    }
30796    
30797    return {
30798        register : function(btn){
30799            if(!btn.toggleGroup){
30800                return;
30801            }
30802            var g = groups[btn.toggleGroup];
30803            if(!g){
30804                g = groups[btn.toggleGroup] = [];
30805            }
30806            g.push(btn);
30807            btn.on("toggle", toggleGroup);
30808        },
30809        
30810        unregister : function(btn){
30811            if(!btn.toggleGroup){
30812                return;
30813            }
30814            var g = groups[btn.toggleGroup];
30815            if(g){
30816                g.remove(btn);
30817                btn.un("toggle", toggleGroup);
30818            }
30819        }
30820    };
30821 }();/*
30822  * Based on:
30823  * Ext JS Library 1.1.1
30824  * Copyright(c) 2006-2007, Ext JS, LLC.
30825  *
30826  * Originally Released Under LGPL - original licence link has changed is not relivant.
30827  *
30828  * Fork - LGPL
30829  * <script type="text/javascript">
30830  */
30831  
30832 /**
30833  * @class Roo.SplitButton
30834  * @extends Roo.Button
30835  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30836  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30837  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30838  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30839  * @cfg {String} arrowTooltip The title attribute of the arrow
30840  * @constructor
30841  * Create a new menu button
30842  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30843  * @param {Object} config The config object
30844  */
30845 Roo.SplitButton = function(renderTo, config){
30846     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30847     /**
30848      * @event arrowclick
30849      * Fires when this button's arrow is clicked
30850      * @param {SplitButton} this
30851      * @param {EventObject} e The click event
30852      */
30853     this.addEvents({"arrowclick":true});
30854 };
30855
30856 Roo.extend(Roo.SplitButton, Roo.Button, {
30857     render : function(renderTo){
30858         // this is one sweet looking template!
30859         var tpl = new Roo.Template(
30860             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30861             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30862             '<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>',
30863             "</tbody></table></td><td>",
30864             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30865             '<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>',
30866             "</tbody></table></td></tr></table>"
30867         );
30868         var btn = tpl.append(renderTo, [this.text, this.type], true);
30869         var btnEl = btn.child("button");
30870         if(this.cls){
30871             btn.addClass(this.cls);
30872         }
30873         if(this.icon){
30874             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30875         }
30876         if(this.iconCls){
30877             btnEl.addClass(this.iconCls);
30878             if(!this.cls){
30879                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30880             }
30881         }
30882         this.el = btn;
30883         if(this.handleMouseEvents){
30884             btn.on("mouseover", this.onMouseOver, this);
30885             btn.on("mouseout", this.onMouseOut, this);
30886             btn.on("mousedown", this.onMouseDown, this);
30887             btn.on("mouseup", this.onMouseUp, this);
30888         }
30889         btn.on(this.clickEvent, this.onClick, this);
30890         if(this.tooltip){
30891             if(typeof this.tooltip == 'object'){
30892                 Roo.QuickTips.tips(Roo.apply({
30893                       target: btnEl.id
30894                 }, this.tooltip));
30895             } else {
30896                 btnEl.dom[this.tooltipType] = this.tooltip;
30897             }
30898         }
30899         if(this.arrowTooltip){
30900             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30901         }
30902         if(this.hidden){
30903             this.hide();
30904         }
30905         if(this.disabled){
30906             this.disable();
30907         }
30908         if(this.pressed){
30909             this.el.addClass("x-btn-pressed");
30910         }
30911         if(Roo.isIE && !Roo.isIE7){
30912             this.autoWidth.defer(1, this);
30913         }else{
30914             this.autoWidth();
30915         }
30916         if(this.menu){
30917             this.menu.on("show", this.onMenuShow, this);
30918             this.menu.on("hide", this.onMenuHide, this);
30919         }
30920         this.fireEvent('render', this);
30921     },
30922
30923     // private
30924     autoWidth : function(){
30925         if(this.el){
30926             var tbl = this.el.child("table:first");
30927             var tbl2 = this.el.child("table:last");
30928             this.el.setWidth("auto");
30929             tbl.setWidth("auto");
30930             if(Roo.isIE7 && Roo.isStrict){
30931                 var ib = this.el.child('button:first');
30932                 if(ib && ib.getWidth() > 20){
30933                     ib.clip();
30934                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30935                 }
30936             }
30937             if(this.minWidth){
30938                 if(this.hidden){
30939                     this.el.beginMeasure();
30940                 }
30941                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30942                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30943                 }
30944                 if(this.hidden){
30945                     this.el.endMeasure();
30946                 }
30947             }
30948             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30949         } 
30950     },
30951     /**
30952      * Sets this button's click handler
30953      * @param {Function} handler The function to call when the button is clicked
30954      * @param {Object} scope (optional) Scope for the function passed above
30955      */
30956     setHandler : function(handler, scope){
30957         this.handler = handler;
30958         this.scope = scope;  
30959     },
30960     
30961     /**
30962      * Sets this button's arrow click handler
30963      * @param {Function} handler The function to call when the arrow is clicked
30964      * @param {Object} scope (optional) Scope for the function passed above
30965      */
30966     setArrowHandler : function(handler, scope){
30967         this.arrowHandler = handler;
30968         this.scope = scope;  
30969     },
30970     
30971     /**
30972      * Focus the button
30973      */
30974     focus : function(){
30975         if(this.el){
30976             this.el.child("button:first").focus();
30977         }
30978     },
30979
30980     // private
30981     onClick : function(e){
30982         e.preventDefault();
30983         if(!this.disabled){
30984             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30985                 if(this.menu && !this.menu.isVisible()){
30986                     this.menu.show(this.el, this.menuAlign);
30987                 }
30988                 this.fireEvent("arrowclick", this, e);
30989                 if(this.arrowHandler){
30990                     this.arrowHandler.call(this.scope || this, this, e);
30991                 }
30992             }else{
30993                 this.fireEvent("click", this, e);
30994                 if(this.handler){
30995                     this.handler.call(this.scope || this, this, e);
30996                 }
30997             }
30998         }
30999     },
31000     // private
31001     onMouseDown : function(e){
31002         if(!this.disabled){
31003             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31004         }
31005     },
31006     // private
31007     onMouseUp : function(e){
31008         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31009     }   
31010 });
31011
31012
31013 // backwards compat
31014 Roo.MenuButton = Roo.SplitButton;/*
31015  * Based on:
31016  * Ext JS Library 1.1.1
31017  * Copyright(c) 2006-2007, Ext JS, LLC.
31018  *
31019  * Originally Released Under LGPL - original licence link has changed is not relivant.
31020  *
31021  * Fork - LGPL
31022  * <script type="text/javascript">
31023  */
31024
31025 /**
31026  * @class Roo.Toolbar
31027  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31028  * Basic Toolbar class.
31029  * @constructor
31030  * Creates a new Toolbar
31031  * @param {Object} container The config object
31032  */ 
31033 Roo.Toolbar = function(container, buttons, config)
31034 {
31035     /// old consturctor format still supported..
31036     if(container instanceof Array){ // omit the container for later rendering
31037         buttons = container;
31038         config = buttons;
31039         container = null;
31040     }
31041     if (typeof(container) == 'object' && container.xtype) {
31042         config = container;
31043         container = config.container;
31044         buttons = config.buttons || []; // not really - use items!!
31045     }
31046     var xitems = [];
31047     if (config && config.items) {
31048         xitems = config.items;
31049         delete config.items;
31050     }
31051     Roo.apply(this, config);
31052     this.buttons = buttons;
31053     
31054     if(container){
31055         this.render(container);
31056     }
31057     this.xitems = xitems;
31058     Roo.each(xitems, function(b) {
31059         this.add(b);
31060     }, this);
31061     
31062 };
31063
31064 Roo.Toolbar.prototype = {
31065     /**
31066      * @cfg {Array} items
31067      * array of button configs or elements to add (will be converted to a MixedCollection)
31068      */
31069     items: false,
31070     /**
31071      * @cfg {String/HTMLElement/Element} container
31072      * The id or element that will contain the toolbar
31073      */
31074     // private
31075     render : function(ct){
31076         this.el = Roo.get(ct);
31077         if(this.cls){
31078             this.el.addClass(this.cls);
31079         }
31080         // using a table allows for vertical alignment
31081         // 100% width is needed by Safari...
31082         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31083         this.tr = this.el.child("tr", true);
31084         var autoId = 0;
31085         this.items = new Roo.util.MixedCollection(false, function(o){
31086             return o.id || ("item" + (++autoId));
31087         });
31088         if(this.buttons){
31089             this.add.apply(this, this.buttons);
31090             delete this.buttons;
31091         }
31092     },
31093
31094     /**
31095      * Adds element(s) to the toolbar -- this function takes a variable number of 
31096      * arguments of mixed type and adds them to the toolbar.
31097      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31098      * <ul>
31099      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31100      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31101      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31102      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31103      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31104      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31105      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31106      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31107      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31108      * </ul>
31109      * @param {Mixed} arg2
31110      * @param {Mixed} etc.
31111      */
31112     add : function(){
31113         var a = arguments, l = a.length;
31114         for(var i = 0; i < l; i++){
31115             this._add(a[i]);
31116         }
31117     },
31118     // private..
31119     _add : function(el) {
31120         
31121         if (el.xtype) {
31122             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31123         }
31124         
31125         if (el.applyTo){ // some kind of form field
31126             return this.addField(el);
31127         } 
31128         if (el.render){ // some kind of Toolbar.Item
31129             return this.addItem(el);
31130         }
31131         if (typeof el == "string"){ // string
31132             if(el == "separator" || el == "-"){
31133                 return this.addSeparator();
31134             }
31135             if (el == " "){
31136                 return this.addSpacer();
31137             }
31138             if(el == "->"){
31139                 return this.addFill();
31140             }
31141             return this.addText(el);
31142             
31143         }
31144         if(el.tagName){ // element
31145             return this.addElement(el);
31146         }
31147         if(typeof el == "object"){ // must be button config?
31148             return this.addButton(el);
31149         }
31150         // and now what?!?!
31151         return false;
31152         
31153     },
31154     
31155     /**
31156      * Add an Xtype element
31157      * @param {Object} xtype Xtype Object
31158      * @return {Object} created Object
31159      */
31160     addxtype : function(e){
31161         return this.add(e);  
31162     },
31163     
31164     /**
31165      * Returns the Element for this toolbar.
31166      * @return {Roo.Element}
31167      */
31168     getEl : function(){
31169         return this.el;  
31170     },
31171     
31172     /**
31173      * Adds a separator
31174      * @return {Roo.Toolbar.Item} The separator item
31175      */
31176     addSeparator : function(){
31177         return this.addItem(new Roo.Toolbar.Separator());
31178     },
31179
31180     /**
31181      * Adds a spacer element
31182      * @return {Roo.Toolbar.Spacer} The spacer item
31183      */
31184     addSpacer : function(){
31185         return this.addItem(new Roo.Toolbar.Spacer());
31186     },
31187
31188     /**
31189      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31190      * @return {Roo.Toolbar.Fill} The fill item
31191      */
31192     addFill : function(){
31193         return this.addItem(new Roo.Toolbar.Fill());
31194     },
31195
31196     /**
31197      * Adds any standard HTML element to the toolbar
31198      * @param {String/HTMLElement/Element} el The element or id of the element to add
31199      * @return {Roo.Toolbar.Item} The element's item
31200      */
31201     addElement : function(el){
31202         return this.addItem(new Roo.Toolbar.Item(el));
31203     },
31204     /**
31205      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31206      * @type Roo.util.MixedCollection  
31207      */
31208     items : false,
31209      
31210     /**
31211      * Adds any Toolbar.Item or subclass
31212      * @param {Roo.Toolbar.Item} item
31213      * @return {Roo.Toolbar.Item} The item
31214      */
31215     addItem : function(item){
31216         var td = this.nextBlock();
31217         item.render(td);
31218         this.items.add(item);
31219         return item;
31220     },
31221     
31222     /**
31223      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31224      * @param {Object/Array} config A button config or array of configs
31225      * @return {Roo.Toolbar.Button/Array}
31226      */
31227     addButton : function(config){
31228         if(config instanceof Array){
31229             var buttons = [];
31230             for(var i = 0, len = config.length; i < len; i++) {
31231                 buttons.push(this.addButton(config[i]));
31232             }
31233             return buttons;
31234         }
31235         var b = config;
31236         if(!(config instanceof Roo.Toolbar.Button)){
31237             b = config.split ?
31238                 new Roo.Toolbar.SplitButton(config) :
31239                 new Roo.Toolbar.Button(config);
31240         }
31241         var td = this.nextBlock();
31242         b.render(td);
31243         this.items.add(b);
31244         return b;
31245     },
31246     
31247     /**
31248      * Adds text to the toolbar
31249      * @param {String} text The text to add
31250      * @return {Roo.Toolbar.Item} The element's item
31251      */
31252     addText : function(text){
31253         return this.addItem(new Roo.Toolbar.TextItem(text));
31254     },
31255     
31256     /**
31257      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31258      * @param {Number} index The index where the item is to be inserted
31259      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31260      * @return {Roo.Toolbar.Button/Item}
31261      */
31262     insertButton : function(index, item){
31263         if(item instanceof Array){
31264             var buttons = [];
31265             for(var i = 0, len = item.length; i < len; i++) {
31266                buttons.push(this.insertButton(index + i, item[i]));
31267             }
31268             return buttons;
31269         }
31270         if (!(item instanceof Roo.Toolbar.Button)){
31271            item = new Roo.Toolbar.Button(item);
31272         }
31273         var td = document.createElement("td");
31274         this.tr.insertBefore(td, this.tr.childNodes[index]);
31275         item.render(td);
31276         this.items.insert(index, item);
31277         return item;
31278     },
31279     
31280     /**
31281      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31282      * @param {Object} config
31283      * @return {Roo.Toolbar.Item} The element's item
31284      */
31285     addDom : function(config, returnEl){
31286         var td = this.nextBlock();
31287         Roo.DomHelper.overwrite(td, config);
31288         var ti = new Roo.Toolbar.Item(td.firstChild);
31289         ti.render(td);
31290         this.items.add(ti);
31291         return ti;
31292     },
31293
31294     /**
31295      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31296      * @type Roo.util.MixedCollection  
31297      */
31298     fields : false,
31299     
31300     /**
31301      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31302      * Note: the field should not have been rendered yet. For a field that has already been
31303      * rendered, use {@link #addElement}.
31304      * @param {Roo.form.Field} field
31305      * @return {Roo.ToolbarItem}
31306      */
31307      
31308       
31309     addField : function(field) {
31310         if (!this.fields) {
31311             var autoId = 0;
31312             this.fields = new Roo.util.MixedCollection(false, function(o){
31313                 return o.id || ("item" + (++autoId));
31314             });
31315
31316         }
31317         
31318         var td = this.nextBlock();
31319         field.render(td);
31320         var ti = new Roo.Toolbar.Item(td.firstChild);
31321         ti.render(td);
31322         this.items.add(ti);
31323         this.fields.add(field);
31324         return ti;
31325     },
31326     /**
31327      * Hide the toolbar
31328      * @method hide
31329      */
31330      
31331       
31332     hide : function()
31333     {
31334         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31335         this.el.child('div').hide();
31336     },
31337     /**
31338      * Show the toolbar
31339      * @method show
31340      */
31341     show : function()
31342     {
31343         this.el.child('div').show();
31344     },
31345       
31346     // private
31347     nextBlock : function(){
31348         var td = document.createElement("td");
31349         this.tr.appendChild(td);
31350         return td;
31351     },
31352
31353     // private
31354     destroy : function(){
31355         if(this.items){ // rendered?
31356             Roo.destroy.apply(Roo, this.items.items);
31357         }
31358         if(this.fields){ // rendered?
31359             Roo.destroy.apply(Roo, this.fields.items);
31360         }
31361         Roo.Element.uncache(this.el, this.tr);
31362     }
31363 };
31364
31365 /**
31366  * @class Roo.Toolbar.Item
31367  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31368  * @constructor
31369  * Creates a new Item
31370  * @param {HTMLElement} el 
31371  */
31372 Roo.Toolbar.Item = function(el){
31373     var cfg = {};
31374     if (typeof (el.xtype) != 'undefined') {
31375         cfg = el;
31376         el = cfg.el;
31377     }
31378     
31379     this.el = Roo.getDom(el);
31380     this.id = Roo.id(this.el);
31381     this.hidden = false;
31382     
31383     this.addEvents({
31384          /**
31385              * @event render
31386              * Fires when the button is rendered
31387              * @param {Button} this
31388              */
31389         'render': true
31390     });
31391     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31392 };
31393 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31394 //Roo.Toolbar.Item.prototype = {
31395     
31396     /**
31397      * Get this item's HTML Element
31398      * @return {HTMLElement}
31399      */
31400     getEl : function(){
31401        return this.el;  
31402     },
31403
31404     // private
31405     render : function(td){
31406         
31407          this.td = td;
31408         td.appendChild(this.el);
31409         
31410         this.fireEvent('render', this);
31411     },
31412     
31413     /**
31414      * Removes and destroys this item.
31415      */
31416     destroy : function(){
31417         this.td.parentNode.removeChild(this.td);
31418     },
31419     
31420     /**
31421      * Shows this item.
31422      */
31423     show: function(){
31424         this.hidden = false;
31425         this.td.style.display = "";
31426     },
31427     
31428     /**
31429      * Hides this item.
31430      */
31431     hide: function(){
31432         this.hidden = true;
31433         this.td.style.display = "none";
31434     },
31435     
31436     /**
31437      * Convenience function for boolean show/hide.
31438      * @param {Boolean} visible true to show/false to hide
31439      */
31440     setVisible: function(visible){
31441         if(visible) {
31442             this.show();
31443         }else{
31444             this.hide();
31445         }
31446     },
31447     
31448     /**
31449      * Try to focus this item.
31450      */
31451     focus : function(){
31452         Roo.fly(this.el).focus();
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         this.el.disabled = true;
31462     },
31463     
31464     /**
31465      * Enables this item.
31466      */
31467     enable : function(){
31468         Roo.fly(this.td).removeClass("x-item-disabled");
31469         this.disabled = false;
31470         this.el.disabled = false;
31471     }
31472 });
31473
31474
31475 /**
31476  * @class Roo.Toolbar.Separator
31477  * @extends Roo.Toolbar.Item
31478  * A simple toolbar separator class
31479  * @constructor
31480  * Creates a new Separator
31481  */
31482 Roo.Toolbar.Separator = function(cfg){
31483     
31484     var s = document.createElement("span");
31485     s.className = "ytb-sep";
31486     if (cfg) {
31487         cfg.el = s;
31488     }
31489     
31490     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31491 };
31492 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31493     enable:Roo.emptyFn,
31494     disable:Roo.emptyFn,
31495     focus:Roo.emptyFn
31496 });
31497
31498 /**
31499  * @class Roo.Toolbar.Spacer
31500  * @extends Roo.Toolbar.Item
31501  * A simple element that adds extra horizontal space to a toolbar.
31502  * @constructor
31503  * Creates a new Spacer
31504  */
31505 Roo.Toolbar.Spacer = function(cfg){
31506     var s = document.createElement("div");
31507     s.className = "ytb-spacer";
31508     if (cfg) {
31509         cfg.el = s;
31510     }
31511     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31512 };
31513 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31514     enable:Roo.emptyFn,
31515     disable:Roo.emptyFn,
31516     focus:Roo.emptyFn
31517 });
31518
31519 /**
31520  * @class Roo.Toolbar.Fill
31521  * @extends Roo.Toolbar.Spacer
31522  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31523  * @constructor
31524  * Creates a new Spacer
31525  */
31526 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31527     // private
31528     render : function(td){
31529         td.style.width = '100%';
31530         Roo.Toolbar.Fill.superclass.render.call(this, td);
31531     }
31532 });
31533
31534 /**
31535  * @class Roo.Toolbar.TextItem
31536  * @extends Roo.Toolbar.Item
31537  * A simple class that renders text directly into a toolbar.
31538  * @constructor
31539  * Creates a new TextItem
31540  * @cfg {string} text 
31541  */
31542 Roo.Toolbar.TextItem = function(cfg){
31543     var  text = cfg || "";
31544     if (typeof(cfg) == 'object') {
31545         text = cfg.text || "";
31546     }  else {
31547         cfg = null;
31548     }
31549     var s = document.createElement("span");
31550     s.className = "ytb-text";
31551     s.innerHTML = text;
31552     if (cfg) {
31553         cfg.el  = s;
31554     }
31555     
31556     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31557 };
31558 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31559     
31560      
31561     enable:Roo.emptyFn,
31562     disable:Roo.emptyFn,
31563     focus:Roo.emptyFn,
31564      /**
31565      * Shows this button
31566      */
31567     show: function(){
31568         this.hidden = false;
31569         this.el.style.display = "";
31570     },
31571     
31572     /**
31573      * Hides this button
31574      */
31575     hide: function(){
31576         this.hidden = true;
31577         this.el.style.display = "none";
31578     }
31579     
31580 });
31581
31582 /**
31583  * @class Roo.Toolbar.Button
31584  * @extends Roo.Button
31585  * A button that renders into a toolbar.
31586  * @constructor
31587  * Creates a new Button
31588  * @param {Object} config A standard {@link Roo.Button} config object
31589  */
31590 Roo.Toolbar.Button = function(config){
31591     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31592 };
31593 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31594 {
31595     
31596     
31597     render : function(td){
31598         this.td = td;
31599         Roo.Toolbar.Button.superclass.render.call(this, td);
31600     },
31601     
31602     /**
31603      * Removes and destroys this button
31604      */
31605     destroy : function(){
31606         Roo.Toolbar.Button.superclass.destroy.call(this);
31607         this.td.parentNode.removeChild(this.td);
31608     },
31609     
31610     /**
31611      * Shows this button
31612      */
31613     show: function(){
31614         this.hidden = false;
31615         this.td.style.display = "";
31616     },
31617     
31618     /**
31619      * Hides this button
31620      */
31621     hide: function(){
31622         this.hidden = true;
31623         this.td.style.display = "none";
31624     },
31625
31626     /**
31627      * Disables this item
31628      */
31629     disable : function(){
31630         Roo.fly(this.td).addClass("x-item-disabled");
31631         this.disabled = true;
31632     },
31633
31634     /**
31635      * Enables this item
31636      */
31637     enable : function(){
31638         Roo.fly(this.td).removeClass("x-item-disabled");
31639         this.disabled = false;
31640     }
31641 });
31642 // backwards compat
31643 Roo.ToolbarButton = Roo.Toolbar.Button;
31644
31645 /**
31646  * @class Roo.Toolbar.SplitButton
31647  * @extends Roo.SplitButton
31648  * A menu button that renders into a toolbar.
31649  * @constructor
31650  * Creates a new SplitButton
31651  * @param {Object} config A standard {@link Roo.SplitButton} config object
31652  */
31653 Roo.Toolbar.SplitButton = function(config){
31654     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31655 };
31656 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31657     render : function(td){
31658         this.td = td;
31659         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31660     },
31661     
31662     /**
31663      * Removes and destroys this button
31664      */
31665     destroy : function(){
31666         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31667         this.td.parentNode.removeChild(this.td);
31668     },
31669     
31670     /**
31671      * Shows this button
31672      */
31673     show: function(){
31674         this.hidden = false;
31675         this.td.style.display = "";
31676     },
31677     
31678     /**
31679      * Hides this button
31680      */
31681     hide: function(){
31682         this.hidden = true;
31683         this.td.style.display = "none";
31684     }
31685 });
31686
31687 // backwards compat
31688 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31689  * Based on:
31690  * Ext JS Library 1.1.1
31691  * Copyright(c) 2006-2007, Ext JS, LLC.
31692  *
31693  * Originally Released Under LGPL - original licence link has changed is not relivant.
31694  *
31695  * Fork - LGPL
31696  * <script type="text/javascript">
31697  */
31698  
31699 /**
31700  * @class Roo.PagingToolbar
31701  * @extends Roo.Toolbar
31702  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31703  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31704  * @constructor
31705  * Create a new PagingToolbar
31706  * @param {Object} config The config object
31707  */
31708 Roo.PagingToolbar = function(el, ds, config)
31709 {
31710     // old args format still supported... - xtype is prefered..
31711     if (typeof(el) == 'object' && el.xtype) {
31712         // created from xtype...
31713         config = el;
31714         ds = el.dataSource;
31715         el = config.container;
31716     }
31717     var items = [];
31718     if (config.items) {
31719         items = config.items;
31720         config.items = [];
31721     }
31722     
31723     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31724     this.ds = ds;
31725     this.cursor = 0;
31726     this.renderButtons(this.el);
31727     this.bind(ds);
31728     
31729     // supprot items array.
31730    
31731     Roo.each(items, function(e) {
31732         this.add(Roo.factory(e));
31733     },this);
31734     
31735 };
31736
31737 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31738    
31739     /**
31740      * @cfg {String/HTMLElement/Element} container
31741      * container The id or element that will contain the toolbar
31742      */
31743     /**
31744      * @cfg {Boolean} displayInfo
31745      * True to display the displayMsg (defaults to false)
31746      */
31747     
31748     
31749     /**
31750      * @cfg {Number} pageSize
31751      * The number of records to display per page (defaults to 20)
31752      */
31753     pageSize: 20,
31754     /**
31755      * @cfg {String} displayMsg
31756      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31757      */
31758     displayMsg : 'Displaying {0} - {1} of {2}',
31759     /**
31760      * @cfg {String} emptyMsg
31761      * The message to display when no records are found (defaults to "No data to display")
31762      */
31763     emptyMsg : 'No data to display',
31764     /**
31765      * Customizable piece of the default paging text (defaults to "Page")
31766      * @type String
31767      */
31768     beforePageText : "Page",
31769     /**
31770      * Customizable piece of the default paging text (defaults to "of %0")
31771      * @type String
31772      */
31773     afterPageText : "of {0}",
31774     /**
31775      * Customizable piece of the default paging text (defaults to "First Page")
31776      * @type String
31777      */
31778     firstText : "First Page",
31779     /**
31780      * Customizable piece of the default paging text (defaults to "Previous Page")
31781      * @type String
31782      */
31783     prevText : "Previous Page",
31784     /**
31785      * Customizable piece of the default paging text (defaults to "Next Page")
31786      * @type String
31787      */
31788     nextText : "Next Page",
31789     /**
31790      * Customizable piece of the default paging text (defaults to "Last Page")
31791      * @type String
31792      */
31793     lastText : "Last Page",
31794     /**
31795      * Customizable piece of the default paging text (defaults to "Refresh")
31796      * @type String
31797      */
31798     refreshText : "Refresh",
31799
31800     // private
31801     renderButtons : function(el){
31802         Roo.PagingToolbar.superclass.render.call(this, el);
31803         this.first = this.addButton({
31804             tooltip: this.firstText,
31805             cls: "x-btn-icon x-grid-page-first",
31806             disabled: true,
31807             handler: this.onClick.createDelegate(this, ["first"])
31808         });
31809         this.prev = this.addButton({
31810             tooltip: this.prevText,
31811             cls: "x-btn-icon x-grid-page-prev",
31812             disabled: true,
31813             handler: this.onClick.createDelegate(this, ["prev"])
31814         });
31815         //this.addSeparator();
31816         this.add(this.beforePageText);
31817         this.field = Roo.get(this.addDom({
31818            tag: "input",
31819            type: "text",
31820            size: "3",
31821            value: "1",
31822            cls: "x-grid-page-number"
31823         }).el);
31824         this.field.on("keydown", this.onPagingKeydown, this);
31825         this.field.on("focus", function(){this.dom.select();});
31826         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31827         this.field.setHeight(18);
31828         //this.addSeparator();
31829         this.next = this.addButton({
31830             tooltip: this.nextText,
31831             cls: "x-btn-icon x-grid-page-next",
31832             disabled: true,
31833             handler: this.onClick.createDelegate(this, ["next"])
31834         });
31835         this.last = this.addButton({
31836             tooltip: this.lastText,
31837             cls: "x-btn-icon x-grid-page-last",
31838             disabled: true,
31839             handler: this.onClick.createDelegate(this, ["last"])
31840         });
31841         //this.addSeparator();
31842         this.loading = this.addButton({
31843             tooltip: this.refreshText,
31844             cls: "x-btn-icon x-grid-loading",
31845             handler: this.onClick.createDelegate(this, ["refresh"])
31846         });
31847
31848         if(this.displayInfo){
31849             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31850         }
31851     },
31852
31853     // private
31854     updateInfo : function(){
31855         if(this.displayEl){
31856             var count = this.ds.getCount();
31857             var msg = count == 0 ?
31858                 this.emptyMsg :
31859                 String.format(
31860                     this.displayMsg,
31861                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31862                 );
31863             this.displayEl.update(msg);
31864         }
31865     },
31866
31867     // private
31868     onLoad : function(ds, r, o){
31869        this.cursor = o.params ? o.params.start : 0;
31870        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31871
31872        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31873        this.field.dom.value = ap;
31874        this.first.setDisabled(ap == 1);
31875        this.prev.setDisabled(ap == 1);
31876        this.next.setDisabled(ap == ps);
31877        this.last.setDisabled(ap == ps);
31878        this.loading.enable();
31879        this.updateInfo();
31880     },
31881
31882     // private
31883     getPageData : function(){
31884         var total = this.ds.getTotalCount();
31885         return {
31886             total : total,
31887             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31888             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31889         };
31890     },
31891
31892     // private
31893     onLoadError : function(){
31894         this.loading.enable();
31895     },
31896
31897     // private
31898     onPagingKeydown : function(e){
31899         var k = e.getKey();
31900         var d = this.getPageData();
31901         if(k == e.RETURN){
31902             var v = this.field.dom.value, pageNum;
31903             if(!v || isNaN(pageNum = parseInt(v, 10))){
31904                 this.field.dom.value = d.activePage;
31905                 return;
31906             }
31907             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31908             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31909             e.stopEvent();
31910         }
31911         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))
31912         {
31913           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31914           this.field.dom.value = pageNum;
31915           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31916           e.stopEvent();
31917         }
31918         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31919         {
31920           var v = this.field.dom.value, pageNum; 
31921           var increment = (e.shiftKey) ? 10 : 1;
31922           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31923             increment *= -1;
31924           }
31925           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31926             this.field.dom.value = d.activePage;
31927             return;
31928           }
31929           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31930           {
31931             this.field.dom.value = parseInt(v, 10) + increment;
31932             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31933             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31934           }
31935           e.stopEvent();
31936         }
31937     },
31938
31939     // private
31940     beforeLoad : function(){
31941         if(this.loading){
31942             this.loading.disable();
31943         }
31944     },
31945     /**
31946      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
31947      * @param {String} which (first|prev|next|last|refresh)  which button to press.
31948      *
31949      */
31950     // private
31951     onClick : function(which){
31952         var ds = this.ds;
31953         switch(which){
31954             case "first":
31955                 ds.load({params:{start: 0, limit: this.pageSize}});
31956             break;
31957             case "prev":
31958                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31959             break;
31960             case "next":
31961                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31962             break;
31963             case "last":
31964                 var total = ds.getTotalCount();
31965                 var extra = total % this.pageSize;
31966                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31967                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31968             break;
31969             case "refresh":
31970                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31971             break;
31972         }
31973     },
31974
31975     /**
31976      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31977      * @param {Roo.data.Store} store The data store to unbind
31978      */
31979     unbind : function(ds){
31980         ds.un("beforeload", this.beforeLoad, this);
31981         ds.un("load", this.onLoad, this);
31982         ds.un("loadexception", this.onLoadError, this);
31983         ds.un("remove", this.updateInfo, this);
31984         ds.un("add", this.updateInfo, this);
31985         this.ds = undefined;
31986     },
31987
31988     /**
31989      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31990      * @param {Roo.data.Store} store The data store to bind
31991      */
31992     bind : function(ds){
31993         ds.on("beforeload", this.beforeLoad, this);
31994         ds.on("load", this.onLoad, this);
31995         ds.on("loadexception", this.onLoadError, this);
31996         ds.on("remove", this.updateInfo, this);
31997         ds.on("add", this.updateInfo, this);
31998         this.ds = ds;
31999     }
32000 });/*
32001  * Based on:
32002  * Ext JS Library 1.1.1
32003  * Copyright(c) 2006-2007, Ext JS, LLC.
32004  *
32005  * Originally Released Under LGPL - original licence link has changed is not relivant.
32006  *
32007  * Fork - LGPL
32008  * <script type="text/javascript">
32009  */
32010
32011 /**
32012  * @class Roo.Resizable
32013  * @extends Roo.util.Observable
32014  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32015  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32016  * 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
32017  * the element will be wrapped for you automatically.</p>
32018  * <p>Here is the list of valid resize handles:</p>
32019  * <pre>
32020 Value   Description
32021 ------  -------------------
32022  'n'     north
32023  's'     south
32024  'e'     east
32025  'w'     west
32026  'nw'    northwest
32027  'sw'    southwest
32028  'se'    southeast
32029  'ne'    northeast
32030  'hd'    horizontal drag
32031  'all'   all
32032 </pre>
32033  * <p>Here's an example showing the creation of a typical Resizable:</p>
32034  * <pre><code>
32035 var resizer = new Roo.Resizable("element-id", {
32036     handles: 'all',
32037     minWidth: 200,
32038     minHeight: 100,
32039     maxWidth: 500,
32040     maxHeight: 400,
32041     pinned: true
32042 });
32043 resizer.on("resize", myHandler);
32044 </code></pre>
32045  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32046  * resizer.east.setDisplayed(false);</p>
32047  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32048  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32049  * resize operation's new size (defaults to [0, 0])
32050  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32051  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32052  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32053  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32054  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32055  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32056  * @cfg {Number} width The width of the element in pixels (defaults to null)
32057  * @cfg {Number} height The height of the element in pixels (defaults to null)
32058  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32059  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32060  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32061  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32062  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32063  * in favor of the handles config option (defaults to false)
32064  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32065  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32066  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32067  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32068  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32069  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32070  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32071  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32072  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32073  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32074  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32075  * @constructor
32076  * Create a new resizable component
32077  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32078  * @param {Object} config configuration options
32079   */
32080 Roo.Resizable = function(el, config)
32081 {
32082     this.el = Roo.get(el);
32083
32084     if(config && config.wrap){
32085         config.resizeChild = this.el;
32086         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32087         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32088         this.el.setStyle("overflow", "hidden");
32089         this.el.setPositioning(config.resizeChild.getPositioning());
32090         config.resizeChild.clearPositioning();
32091         if(!config.width || !config.height){
32092             var csize = config.resizeChild.getSize();
32093             this.el.setSize(csize.width, csize.height);
32094         }
32095         if(config.pinned && !config.adjustments){
32096             config.adjustments = "auto";
32097         }
32098     }
32099
32100     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32101     this.proxy.unselectable();
32102     this.proxy.enableDisplayMode('block');
32103
32104     Roo.apply(this, config);
32105
32106     if(this.pinned){
32107         this.disableTrackOver = true;
32108         this.el.addClass("x-resizable-pinned");
32109     }
32110     // if the element isn't positioned, make it relative
32111     var position = this.el.getStyle("position");
32112     if(position != "absolute" && position != "fixed"){
32113         this.el.setStyle("position", "relative");
32114     }
32115     if(!this.handles){ // no handles passed, must be legacy style
32116         this.handles = 's,e,se';
32117         if(this.multiDirectional){
32118             this.handles += ',n,w';
32119         }
32120     }
32121     if(this.handles == "all"){
32122         this.handles = "n s e w ne nw se sw";
32123     }
32124     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32125     var ps = Roo.Resizable.positions;
32126     for(var i = 0, len = hs.length; i < len; i++){
32127         if(hs[i] && ps[hs[i]]){
32128             var pos = ps[hs[i]];
32129             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32130         }
32131     }
32132     // legacy
32133     this.corner = this.southeast;
32134     
32135     // updateBox = the box can move..
32136     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32137         this.updateBox = true;
32138     }
32139
32140     this.activeHandle = null;
32141
32142     if(this.resizeChild){
32143         if(typeof this.resizeChild == "boolean"){
32144             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32145         }else{
32146             this.resizeChild = Roo.get(this.resizeChild, true);
32147         }
32148     }
32149     
32150     if(this.adjustments == "auto"){
32151         var rc = this.resizeChild;
32152         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32153         if(rc && (hw || hn)){
32154             rc.position("relative");
32155             rc.setLeft(hw ? hw.el.getWidth() : 0);
32156             rc.setTop(hn ? hn.el.getHeight() : 0);
32157         }
32158         this.adjustments = [
32159             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32160             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32161         ];
32162     }
32163
32164     if(this.draggable){
32165         this.dd = this.dynamic ?
32166             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32167         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32168     }
32169
32170     // public events
32171     this.addEvents({
32172         /**
32173          * @event beforeresize
32174          * Fired before resize is allowed. Set enabled to false to cancel resize.
32175          * @param {Roo.Resizable} this
32176          * @param {Roo.EventObject} e The mousedown event
32177          */
32178         "beforeresize" : true,
32179         /**
32180          * @event resizing
32181          * Fired a resizing.
32182          * @param {Roo.Resizable} this
32183          * @param {Number} x The new x position
32184          * @param {Number} y The new y position
32185          * @param {Number} w The new w width
32186          * @param {Number} h The new h hight
32187          * @param {Roo.EventObject} e The mouseup event
32188          */
32189         "resizing" : true,
32190         /**
32191          * @event resize
32192          * Fired after a resize.
32193          * @param {Roo.Resizable} this
32194          * @param {Number} width The new width
32195          * @param {Number} height The new height
32196          * @param {Roo.EventObject} e The mouseup event
32197          */
32198         "resize" : true
32199     });
32200
32201     if(this.width !== null && this.height !== null){
32202         this.resizeTo(this.width, this.height);
32203     }else{
32204         this.updateChildSize();
32205     }
32206     if(Roo.isIE){
32207         this.el.dom.style.zoom = 1;
32208     }
32209     Roo.Resizable.superclass.constructor.call(this);
32210 };
32211
32212 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32213         resizeChild : false,
32214         adjustments : [0, 0],
32215         minWidth : 5,
32216         minHeight : 5,
32217         maxWidth : 10000,
32218         maxHeight : 10000,
32219         enabled : true,
32220         animate : false,
32221         duration : .35,
32222         dynamic : false,
32223         handles : false,
32224         multiDirectional : false,
32225         disableTrackOver : false,
32226         easing : 'easeOutStrong',
32227         widthIncrement : 0,
32228         heightIncrement : 0,
32229         pinned : false,
32230         width : null,
32231         height : null,
32232         preserveRatio : false,
32233         transparent: false,
32234         minX: 0,
32235         minY: 0,
32236         draggable: false,
32237
32238         /**
32239          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32240          */
32241         constrainTo: undefined,
32242         /**
32243          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32244          */
32245         resizeRegion: undefined,
32246
32247
32248     /**
32249      * Perform a manual resize
32250      * @param {Number} width
32251      * @param {Number} height
32252      */
32253     resizeTo : function(width, height){
32254         this.el.setSize(width, height);
32255         this.updateChildSize();
32256         this.fireEvent("resize", this, width, height, null);
32257     },
32258
32259     // private
32260     startSizing : function(e, handle){
32261         this.fireEvent("beforeresize", this, e);
32262         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32263
32264             if(!this.overlay){
32265                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32266                 this.overlay.unselectable();
32267                 this.overlay.enableDisplayMode("block");
32268                 this.overlay.on("mousemove", this.onMouseMove, this);
32269                 this.overlay.on("mouseup", this.onMouseUp, this);
32270             }
32271             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32272
32273             this.resizing = true;
32274             this.startBox = this.el.getBox();
32275             this.startPoint = e.getXY();
32276             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32277                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32278
32279             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32280             this.overlay.show();
32281
32282             if(this.constrainTo) {
32283                 var ct = Roo.get(this.constrainTo);
32284                 this.resizeRegion = ct.getRegion().adjust(
32285                     ct.getFrameWidth('t'),
32286                     ct.getFrameWidth('l'),
32287                     -ct.getFrameWidth('b'),
32288                     -ct.getFrameWidth('r')
32289                 );
32290             }
32291
32292             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32293             this.proxy.show();
32294             this.proxy.setBox(this.startBox);
32295             if(!this.dynamic){
32296                 this.proxy.setStyle('visibility', 'visible');
32297             }
32298         }
32299     },
32300
32301     // private
32302     onMouseDown : function(handle, e){
32303         if(this.enabled){
32304             e.stopEvent();
32305             this.activeHandle = handle;
32306             this.startSizing(e, handle);
32307         }
32308     },
32309
32310     // private
32311     onMouseUp : function(e){
32312         var size = this.resizeElement();
32313         this.resizing = false;
32314         this.handleOut();
32315         this.overlay.hide();
32316         this.proxy.hide();
32317         this.fireEvent("resize", this, size.width, size.height, e);
32318     },
32319
32320     // private
32321     updateChildSize : function(){
32322         
32323         if(this.resizeChild){
32324             var el = this.el;
32325             var child = this.resizeChild;
32326             var adj = this.adjustments;
32327             if(el.dom.offsetWidth){
32328                 var b = el.getSize(true);
32329                 child.setSize(b.width+adj[0], b.height+adj[1]);
32330             }
32331             // Second call here for IE
32332             // The first call enables instant resizing and
32333             // the second call corrects scroll bars if they
32334             // exist
32335             if(Roo.isIE){
32336                 setTimeout(function(){
32337                     if(el.dom.offsetWidth){
32338                         var b = el.getSize(true);
32339                         child.setSize(b.width+adj[0], b.height+adj[1]);
32340                     }
32341                 }, 10);
32342             }
32343         }
32344     },
32345
32346     // private
32347     snap : function(value, inc, min){
32348         if(!inc || !value) {
32349             return value;
32350         }
32351         var newValue = value;
32352         var m = value % inc;
32353         if(m > 0){
32354             if(m > (inc/2)){
32355                 newValue = value + (inc-m);
32356             }else{
32357                 newValue = value - m;
32358             }
32359         }
32360         return Math.max(min, newValue);
32361     },
32362
32363     // private
32364     resizeElement : function(){
32365         var box = this.proxy.getBox();
32366         if(this.updateBox){
32367             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32368         }else{
32369             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32370         }
32371         this.updateChildSize();
32372         if(!this.dynamic){
32373             this.proxy.hide();
32374         }
32375         return box;
32376     },
32377
32378     // private
32379     constrain : function(v, diff, m, mx){
32380         if(v - diff < m){
32381             diff = v - m;
32382         }else if(v - diff > mx){
32383             diff = mx - v;
32384         }
32385         return diff;
32386     },
32387
32388     // private
32389     onMouseMove : function(e){
32390         
32391         if(this.enabled){
32392             try{// try catch so if something goes wrong the user doesn't get hung
32393
32394             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32395                 return;
32396             }
32397
32398             //var curXY = this.startPoint;
32399             var curSize = this.curSize || this.startBox;
32400             var x = this.startBox.x, y = this.startBox.y;
32401             var ox = x, oy = y;
32402             var w = curSize.width, h = curSize.height;
32403             var ow = w, oh = h;
32404             var mw = this.minWidth, mh = this.minHeight;
32405             var mxw = this.maxWidth, mxh = this.maxHeight;
32406             var wi = this.widthIncrement;
32407             var hi = this.heightIncrement;
32408
32409             var eventXY = e.getXY();
32410             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32411             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32412
32413             var pos = this.activeHandle.position;
32414
32415             switch(pos){
32416                 case "east":
32417                     w += diffX;
32418                     w = Math.min(Math.max(mw, w), mxw);
32419                     break;
32420              
32421                 case "south":
32422                     h += diffY;
32423                     h = Math.min(Math.max(mh, h), mxh);
32424                     break;
32425                 case "southeast":
32426                     w += diffX;
32427                     h += diffY;
32428                     w = Math.min(Math.max(mw, w), mxw);
32429                     h = Math.min(Math.max(mh, h), mxh);
32430                     break;
32431                 case "north":
32432                     diffY = this.constrain(h, diffY, mh, mxh);
32433                     y += diffY;
32434                     h -= diffY;
32435                     break;
32436                 case "hdrag":
32437                     
32438                     if (wi) {
32439                         var adiffX = Math.abs(diffX);
32440                         var sub = (adiffX % wi); // how much 
32441                         if (sub > (wi/2)) { // far enough to snap
32442                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32443                         } else {
32444                             // remove difference.. 
32445                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32446                         }
32447                     }
32448                     x += diffX;
32449                     x = Math.max(this.minX, x);
32450                     break;
32451                 case "west":
32452                     diffX = this.constrain(w, diffX, mw, mxw);
32453                     x += diffX;
32454                     w -= diffX;
32455                     break;
32456                 case "northeast":
32457                     w += diffX;
32458                     w = Math.min(Math.max(mw, w), mxw);
32459                     diffY = this.constrain(h, diffY, mh, mxh);
32460                     y += diffY;
32461                     h -= diffY;
32462                     break;
32463                 case "northwest":
32464                     diffX = this.constrain(w, diffX, mw, mxw);
32465                     diffY = this.constrain(h, diffY, mh, mxh);
32466                     y += diffY;
32467                     h -= diffY;
32468                     x += diffX;
32469                     w -= diffX;
32470                     break;
32471                case "southwest":
32472                     diffX = this.constrain(w, diffX, mw, mxw);
32473                     h += diffY;
32474                     h = Math.min(Math.max(mh, h), mxh);
32475                     x += diffX;
32476                     w -= diffX;
32477                     break;
32478             }
32479
32480             var sw = this.snap(w, wi, mw);
32481             var sh = this.snap(h, hi, mh);
32482             if(sw != w || sh != h){
32483                 switch(pos){
32484                     case "northeast":
32485                         y -= sh - h;
32486                     break;
32487                     case "north":
32488                         y -= sh - h;
32489                         break;
32490                     case "southwest":
32491                         x -= sw - w;
32492                     break;
32493                     case "west":
32494                         x -= sw - w;
32495                         break;
32496                     case "northwest":
32497                         x -= sw - w;
32498                         y -= sh - h;
32499                     break;
32500                 }
32501                 w = sw;
32502                 h = sh;
32503             }
32504
32505             if(this.preserveRatio){
32506                 switch(pos){
32507                     case "southeast":
32508                     case "east":
32509                         h = oh * (w/ow);
32510                         h = Math.min(Math.max(mh, h), mxh);
32511                         w = ow * (h/oh);
32512                        break;
32513                     case "south":
32514                         w = ow * (h/oh);
32515                         w = Math.min(Math.max(mw, w), mxw);
32516                         h = oh * (w/ow);
32517                         break;
32518                     case "northeast":
32519                         w = ow * (h/oh);
32520                         w = Math.min(Math.max(mw, w), mxw);
32521                         h = oh * (w/ow);
32522                     break;
32523                     case "north":
32524                         var tw = w;
32525                         w = ow * (h/oh);
32526                         w = Math.min(Math.max(mw, w), mxw);
32527                         h = oh * (w/ow);
32528                         x += (tw - w) / 2;
32529                         break;
32530                     case "southwest":
32531                         h = oh * (w/ow);
32532                         h = Math.min(Math.max(mh, h), mxh);
32533                         var tw = w;
32534                         w = ow * (h/oh);
32535                         x += tw - w;
32536                         break;
32537                     case "west":
32538                         var th = h;
32539                         h = oh * (w/ow);
32540                         h = Math.min(Math.max(mh, h), mxh);
32541                         y += (th - h) / 2;
32542                         var tw = w;
32543                         w = ow * (h/oh);
32544                         x += tw - w;
32545                        break;
32546                     case "northwest":
32547                         var tw = w;
32548                         var th = h;
32549                         h = oh * (w/ow);
32550                         h = Math.min(Math.max(mh, h), mxh);
32551                         w = ow * (h/oh);
32552                         y += th - h;
32553                         x += tw - w;
32554                        break;
32555
32556                 }
32557             }
32558             if (pos == 'hdrag') {
32559                 w = ow;
32560             }
32561             this.proxy.setBounds(x, y, w, h);
32562             if(this.dynamic){
32563                 this.resizeElement();
32564             }
32565             }catch(e){}
32566         }
32567         this.fireEvent("resizing", this, x, y, w, h, e);
32568     },
32569
32570     // private
32571     handleOver : function(){
32572         if(this.enabled){
32573             this.el.addClass("x-resizable-over");
32574         }
32575     },
32576
32577     // private
32578     handleOut : function(){
32579         if(!this.resizing){
32580             this.el.removeClass("x-resizable-over");
32581         }
32582     },
32583
32584     /**
32585      * Returns the element this component is bound to.
32586      * @return {Roo.Element}
32587      */
32588     getEl : function(){
32589         return this.el;
32590     },
32591
32592     /**
32593      * Returns the resizeChild element (or null).
32594      * @return {Roo.Element}
32595      */
32596     getResizeChild : function(){
32597         return this.resizeChild;
32598     },
32599     groupHandler : function()
32600     {
32601         
32602     },
32603     /**
32604      * Destroys this resizable. If the element was wrapped and
32605      * removeEl is not true then the element remains.
32606      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32607      */
32608     destroy : function(removeEl){
32609         this.proxy.remove();
32610         if(this.overlay){
32611             this.overlay.removeAllListeners();
32612             this.overlay.remove();
32613         }
32614         var ps = Roo.Resizable.positions;
32615         for(var k in ps){
32616             if(typeof ps[k] != "function" && this[ps[k]]){
32617                 var h = this[ps[k]];
32618                 h.el.removeAllListeners();
32619                 h.el.remove();
32620             }
32621         }
32622         if(removeEl){
32623             this.el.update("");
32624             this.el.remove();
32625         }
32626     }
32627 });
32628
32629 // private
32630 // hash to map config positions to true positions
32631 Roo.Resizable.positions = {
32632     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32633     hd: "hdrag"
32634 };
32635
32636 // private
32637 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32638     if(!this.tpl){
32639         // only initialize the template if resizable is used
32640         var tpl = Roo.DomHelper.createTemplate(
32641             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32642         );
32643         tpl.compile();
32644         Roo.Resizable.Handle.prototype.tpl = tpl;
32645     }
32646     this.position = pos;
32647     this.rz = rz;
32648     // show north drag fro topdra
32649     var handlepos = pos == 'hdrag' ? 'north' : pos;
32650     
32651     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32652     if (pos == 'hdrag') {
32653         this.el.setStyle('cursor', 'pointer');
32654     }
32655     this.el.unselectable();
32656     if(transparent){
32657         this.el.setOpacity(0);
32658     }
32659     this.el.on("mousedown", this.onMouseDown, this);
32660     if(!disableTrackOver){
32661         this.el.on("mouseover", this.onMouseOver, this);
32662         this.el.on("mouseout", this.onMouseOut, this);
32663     }
32664 };
32665
32666 // private
32667 Roo.Resizable.Handle.prototype = {
32668     afterResize : function(rz){
32669         Roo.log('after?');
32670         // do nothing
32671     },
32672     // private
32673     onMouseDown : function(e){
32674         this.rz.onMouseDown(this, e);
32675     },
32676     // private
32677     onMouseOver : function(e){
32678         this.rz.handleOver(this, e);
32679     },
32680     // private
32681     onMouseOut : function(e){
32682         this.rz.handleOut(this, e);
32683     }
32684 };/*
32685  * Based on:
32686  * Ext JS Library 1.1.1
32687  * Copyright(c) 2006-2007, Ext JS, LLC.
32688  *
32689  * Originally Released Under LGPL - original licence link has changed is not relivant.
32690  *
32691  * Fork - LGPL
32692  * <script type="text/javascript">
32693  */
32694
32695 /**
32696  * @class Roo.Editor
32697  * @extends Roo.Component
32698  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32699  * @constructor
32700  * Create a new Editor
32701  * @param {Roo.form.Field} field The Field object (or descendant)
32702  * @param {Object} config The config object
32703  */
32704 Roo.Editor = function(field, config){
32705     Roo.Editor.superclass.constructor.call(this, config);
32706     this.field = field;
32707     this.addEvents({
32708         /**
32709              * @event beforestartedit
32710              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32711              * false from the handler of this event.
32712              * @param {Editor} this
32713              * @param {Roo.Element} boundEl The underlying element bound to this editor
32714              * @param {Mixed} value The field value being set
32715              */
32716         "beforestartedit" : true,
32717         /**
32718              * @event startedit
32719              * Fires when this editor is displayed
32720              * @param {Roo.Element} boundEl The underlying element bound to this editor
32721              * @param {Mixed} value The starting field value
32722              */
32723         "startedit" : true,
32724         /**
32725              * @event beforecomplete
32726              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32727              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32728              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32729              * event will not fire since no edit actually occurred.
32730              * @param {Editor} this
32731              * @param {Mixed} value The current field value
32732              * @param {Mixed} startValue The original field value
32733              */
32734         "beforecomplete" : true,
32735         /**
32736              * @event complete
32737              * Fires after editing is complete and any changed value has been written to the underlying field.
32738              * @param {Editor} this
32739              * @param {Mixed} value The current field value
32740              * @param {Mixed} startValue The original field value
32741              */
32742         "complete" : true,
32743         /**
32744          * @event specialkey
32745          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32746          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32747          * @param {Roo.form.Field} this
32748          * @param {Roo.EventObject} e The event object
32749          */
32750         "specialkey" : true
32751     });
32752 };
32753
32754 Roo.extend(Roo.Editor, Roo.Component, {
32755     /**
32756      * @cfg {Boolean/String} autosize
32757      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32758      * or "height" to adopt the height only (defaults to false)
32759      */
32760     /**
32761      * @cfg {Boolean} revertInvalid
32762      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32763      * validation fails (defaults to true)
32764      */
32765     /**
32766      * @cfg {Boolean} ignoreNoChange
32767      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32768      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32769      * will never be ignored.
32770      */
32771     /**
32772      * @cfg {Boolean} hideEl
32773      * False to keep the bound element visible while the editor is displayed (defaults to true)
32774      */
32775     /**
32776      * @cfg {Mixed} value
32777      * The data value of the underlying field (defaults to "")
32778      */
32779     value : "",
32780     /**
32781      * @cfg {String} alignment
32782      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32783      */
32784     alignment: "c-c?",
32785     /**
32786      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32787      * for bottom-right shadow (defaults to "frame")
32788      */
32789     shadow : "frame",
32790     /**
32791      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32792      */
32793     constrain : false,
32794     /**
32795      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32796      */
32797     completeOnEnter : false,
32798     /**
32799      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32800      */
32801     cancelOnEsc : false,
32802     /**
32803      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32804      */
32805     updateEl : false,
32806
32807     // private
32808     onRender : function(ct, position){
32809         this.el = new Roo.Layer({
32810             shadow: this.shadow,
32811             cls: "x-editor",
32812             parentEl : ct,
32813             shim : this.shim,
32814             shadowOffset:4,
32815             id: this.id,
32816             constrain: this.constrain
32817         });
32818         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32819         if(this.field.msgTarget != 'title'){
32820             this.field.msgTarget = 'qtip';
32821         }
32822         this.field.render(this.el);
32823         if(Roo.isGecko){
32824             this.field.el.dom.setAttribute('autocomplete', 'off');
32825         }
32826         this.field.on("specialkey", this.onSpecialKey, this);
32827         if(this.swallowKeys){
32828             this.field.el.swallowEvent(['keydown','keypress']);
32829         }
32830         this.field.show();
32831         this.field.on("blur", this.onBlur, this);
32832         if(this.field.grow){
32833             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32834         }
32835     },
32836
32837     onSpecialKey : function(field, e)
32838     {
32839         //Roo.log('editor onSpecialKey');
32840         if(this.completeOnEnter && e.getKey() == e.ENTER){
32841             e.stopEvent();
32842             this.completeEdit();
32843             return;
32844         }
32845         // do not fire special key otherwise it might hide close the editor...
32846         if(e.getKey() == e.ENTER){    
32847             return;
32848         }
32849         if(this.cancelOnEsc && e.getKey() == e.ESC){
32850             this.cancelEdit();
32851             return;
32852         } 
32853         this.fireEvent('specialkey', field, e);
32854     
32855     },
32856
32857     /**
32858      * Starts the editing process and shows the editor.
32859      * @param {String/HTMLElement/Element} el The element to edit
32860      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32861       * to the innerHTML of el.
32862      */
32863     startEdit : function(el, value){
32864         if(this.editing){
32865             this.completeEdit();
32866         }
32867         this.boundEl = Roo.get(el);
32868         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32869         if(!this.rendered){
32870             this.render(this.parentEl || document.body);
32871         }
32872         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32873             return;
32874         }
32875         this.startValue = v;
32876         this.field.setValue(v);
32877         if(this.autoSize){
32878             var sz = this.boundEl.getSize();
32879             switch(this.autoSize){
32880                 case "width":
32881                 this.setSize(sz.width,  "");
32882                 break;
32883                 case "height":
32884                 this.setSize("",  sz.height);
32885                 break;
32886                 default:
32887                 this.setSize(sz.width,  sz.height);
32888             }
32889         }
32890         this.el.alignTo(this.boundEl, this.alignment);
32891         this.editing = true;
32892         if(Roo.QuickTips){
32893             Roo.QuickTips.disable();
32894         }
32895         this.show();
32896     },
32897
32898     /**
32899      * Sets the height and width of this editor.
32900      * @param {Number} width The new width
32901      * @param {Number} height The new height
32902      */
32903     setSize : function(w, h){
32904         this.field.setSize(w, h);
32905         if(this.el){
32906             this.el.sync();
32907         }
32908     },
32909
32910     /**
32911      * Realigns the editor to the bound field based on the current alignment config value.
32912      */
32913     realign : function(){
32914         this.el.alignTo(this.boundEl, this.alignment);
32915     },
32916
32917     /**
32918      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32919      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32920      */
32921     completeEdit : function(remainVisible){
32922         if(!this.editing){
32923             return;
32924         }
32925         var v = this.getValue();
32926         if(this.revertInvalid !== false && !this.field.isValid()){
32927             v = this.startValue;
32928             this.cancelEdit(true);
32929         }
32930         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32931             this.editing = false;
32932             this.hide();
32933             return;
32934         }
32935         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32936             this.editing = false;
32937             if(this.updateEl && this.boundEl){
32938                 this.boundEl.update(v);
32939             }
32940             if(remainVisible !== true){
32941                 this.hide();
32942             }
32943             this.fireEvent("complete", this, v, this.startValue);
32944         }
32945     },
32946
32947     // private
32948     onShow : function(){
32949         this.el.show();
32950         if(this.hideEl !== false){
32951             this.boundEl.hide();
32952         }
32953         this.field.show();
32954         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32955             this.fixIEFocus = true;
32956             this.deferredFocus.defer(50, this);
32957         }else{
32958             this.field.focus();
32959         }
32960         this.fireEvent("startedit", this.boundEl, this.startValue);
32961     },
32962
32963     deferredFocus : function(){
32964         if(this.editing){
32965             this.field.focus();
32966         }
32967     },
32968
32969     /**
32970      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32971      * reverted to the original starting value.
32972      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32973      * cancel (defaults to false)
32974      */
32975     cancelEdit : function(remainVisible){
32976         if(this.editing){
32977             this.setValue(this.startValue);
32978             if(remainVisible !== true){
32979                 this.hide();
32980             }
32981         }
32982     },
32983
32984     // private
32985     onBlur : function(){
32986         if(this.allowBlur !== true && this.editing){
32987             this.completeEdit();
32988         }
32989     },
32990
32991     // private
32992     onHide : function(){
32993         if(this.editing){
32994             this.completeEdit();
32995             return;
32996         }
32997         this.field.blur();
32998         if(this.field.collapse){
32999             this.field.collapse();
33000         }
33001         this.el.hide();
33002         if(this.hideEl !== false){
33003             this.boundEl.show();
33004         }
33005         if(Roo.QuickTips){
33006             Roo.QuickTips.enable();
33007         }
33008     },
33009
33010     /**
33011      * Sets the data value of the editor
33012      * @param {Mixed} value Any valid value supported by the underlying field
33013      */
33014     setValue : function(v){
33015         this.field.setValue(v);
33016     },
33017
33018     /**
33019      * Gets the data value of the editor
33020      * @return {Mixed} The data value
33021      */
33022     getValue : function(){
33023         return this.field.getValue();
33024     }
33025 });/*
33026  * Based on:
33027  * Ext JS Library 1.1.1
33028  * Copyright(c) 2006-2007, Ext JS, LLC.
33029  *
33030  * Originally Released Under LGPL - original licence link has changed is not relivant.
33031  *
33032  * Fork - LGPL
33033  * <script type="text/javascript">
33034  */
33035  
33036 /**
33037  * @class Roo.BasicDialog
33038  * @extends Roo.util.Observable
33039  * @parent none builder
33040  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33041  * <pre><code>
33042 var dlg = new Roo.BasicDialog("my-dlg", {
33043     height: 200,
33044     width: 300,
33045     minHeight: 100,
33046     minWidth: 150,
33047     modal: true,
33048     proxyDrag: true,
33049     shadow: true
33050 });
33051 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33052 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33053 dlg.addButton('Cancel', dlg.hide, dlg);
33054 dlg.show();
33055 </code></pre>
33056   <b>A Dialog should always be a direct child of the body element.</b>
33057  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33058  * @cfg {String} title Default text to display in the title bar (defaults to null)
33059  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33060  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33061  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33062  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33063  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33064  * (defaults to null with no animation)
33065  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33066  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33067  * property for valid values (defaults to 'all')
33068  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33069  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33070  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33071  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33072  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33073  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33074  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33075  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33076  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33077  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33078  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33079  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33080  * draggable = true (defaults to false)
33081  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33082  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33083  * shadow (defaults to false)
33084  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33085  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33086  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33087  * @cfg {Array} buttons Array of buttons
33088  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33089  * @constructor
33090  * Create a new BasicDialog.
33091  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33092  * @param {Object} config Configuration options
33093  */
33094 Roo.BasicDialog = function(el, config){
33095     this.el = Roo.get(el);
33096     var dh = Roo.DomHelper;
33097     if(!this.el && config && config.autoCreate){
33098         if(typeof config.autoCreate == "object"){
33099             if(!config.autoCreate.id){
33100                 config.autoCreate.id = el;
33101             }
33102             this.el = dh.append(document.body,
33103                         config.autoCreate, true);
33104         }else{
33105             this.el = dh.append(document.body,
33106                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33107         }
33108     }
33109     el = this.el;
33110     el.setDisplayed(true);
33111     el.hide = this.hideAction;
33112     this.id = el.id;
33113     el.addClass("x-dlg");
33114
33115     Roo.apply(this, config);
33116
33117     this.proxy = el.createProxy("x-dlg-proxy");
33118     this.proxy.hide = this.hideAction;
33119     this.proxy.setOpacity(.5);
33120     this.proxy.hide();
33121
33122     if(config.width){
33123         el.setWidth(config.width);
33124     }
33125     if(config.height){
33126         el.setHeight(config.height);
33127     }
33128     this.size = el.getSize();
33129     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33130         this.xy = [config.x,config.y];
33131     }else{
33132         this.xy = el.getCenterXY(true);
33133     }
33134     /** The header element @type Roo.Element */
33135     this.header = el.child("> .x-dlg-hd");
33136     /** The body element @type Roo.Element */
33137     this.body = el.child("> .x-dlg-bd");
33138     /** The footer element @type Roo.Element */
33139     this.footer = el.child("> .x-dlg-ft");
33140
33141     if(!this.header){
33142         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33143     }
33144     if(!this.body){
33145         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33146     }
33147
33148     this.header.unselectable();
33149     if(this.title){
33150         this.header.update(this.title);
33151     }
33152     // this element allows the dialog to be focused for keyboard event
33153     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33154     this.focusEl.swallowEvent("click", true);
33155
33156     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33157
33158     // wrap the body and footer for special rendering
33159     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33160     if(this.footer){
33161         this.bwrap.dom.appendChild(this.footer.dom);
33162     }
33163
33164     this.bg = this.el.createChild({
33165         tag: "div", cls:"x-dlg-bg",
33166         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33167     });
33168     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33169
33170
33171     if(this.autoScroll !== false && !this.autoTabs){
33172         this.body.setStyle("overflow", "auto");
33173     }
33174
33175     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33176
33177     if(this.closable !== false){
33178         this.el.addClass("x-dlg-closable");
33179         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33180         this.close.on("click", this.closeClick, this);
33181         this.close.addClassOnOver("x-dlg-close-over");
33182     }
33183     if(this.collapsible !== false){
33184         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33185         this.collapseBtn.on("click", this.collapseClick, this);
33186         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33187         this.header.on("dblclick", this.collapseClick, this);
33188     }
33189     if(this.resizable !== false){
33190         this.el.addClass("x-dlg-resizable");
33191         this.resizer = new Roo.Resizable(el, {
33192             minWidth: this.minWidth || 80,
33193             minHeight:this.minHeight || 80,
33194             handles: this.resizeHandles || "all",
33195             pinned: true
33196         });
33197         this.resizer.on("beforeresize", this.beforeResize, this);
33198         this.resizer.on("resize", this.onResize, this);
33199     }
33200     if(this.draggable !== false){
33201         el.addClass("x-dlg-draggable");
33202         if (!this.proxyDrag) {
33203             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33204         }
33205         else {
33206             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33207         }
33208         dd.setHandleElId(this.header.id);
33209         dd.endDrag = this.endMove.createDelegate(this);
33210         dd.startDrag = this.startMove.createDelegate(this);
33211         dd.onDrag = this.onDrag.createDelegate(this);
33212         dd.scroll = false;
33213         this.dd = dd;
33214     }
33215     if(this.modal){
33216         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33217         this.mask.enableDisplayMode("block");
33218         this.mask.hide();
33219         this.el.addClass("x-dlg-modal");
33220     }
33221     if(this.shadow){
33222         this.shadow = new Roo.Shadow({
33223             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33224             offset : this.shadowOffset
33225         });
33226     }else{
33227         this.shadowOffset = 0;
33228     }
33229     if(Roo.useShims && this.shim !== false){
33230         this.shim = this.el.createShim();
33231         this.shim.hide = this.hideAction;
33232         this.shim.hide();
33233     }else{
33234         this.shim = false;
33235     }
33236     if(this.autoTabs){
33237         this.initTabs();
33238     }
33239     if (this.buttons) { 
33240         var bts= this.buttons;
33241         this.buttons = [];
33242         Roo.each(bts, function(b) {
33243             this.addButton(b);
33244         }, this);
33245     }
33246     
33247     
33248     this.addEvents({
33249         /**
33250          * @event keydown
33251          * Fires when a key is pressed
33252          * @param {Roo.BasicDialog} this
33253          * @param {Roo.EventObject} e
33254          */
33255         "keydown" : true,
33256         /**
33257          * @event move
33258          * Fires when this dialog is moved by the user.
33259          * @param {Roo.BasicDialog} this
33260          * @param {Number} x The new page X
33261          * @param {Number} y The new page Y
33262          */
33263         "move" : true,
33264         /**
33265          * @event resize
33266          * Fires when this dialog is resized by the user.
33267          * @param {Roo.BasicDialog} this
33268          * @param {Number} width The new width
33269          * @param {Number} height The new height
33270          */
33271         "resize" : true,
33272         /**
33273          * @event beforehide
33274          * Fires before this dialog is hidden.
33275          * @param {Roo.BasicDialog} this
33276          */
33277         "beforehide" : true,
33278         /**
33279          * @event hide
33280          * Fires when this dialog is hidden.
33281          * @param {Roo.BasicDialog} this
33282          */
33283         "hide" : true,
33284         /**
33285          * @event beforeshow
33286          * Fires before this dialog is shown.
33287          * @param {Roo.BasicDialog} this
33288          */
33289         "beforeshow" : true,
33290         /**
33291          * @event show
33292          * Fires when this dialog is shown.
33293          * @param {Roo.BasicDialog} this
33294          */
33295         "show" : true
33296     });
33297     el.on("keydown", this.onKeyDown, this);
33298     el.on("mousedown", this.toFront, this);
33299     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33300     this.el.hide();
33301     Roo.DialogManager.register(this);
33302     Roo.BasicDialog.superclass.constructor.call(this);
33303 };
33304
33305 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33306     shadowOffset: Roo.isIE ? 6 : 5,
33307     minHeight: 80,
33308     minWidth: 200,
33309     minButtonWidth: 75,
33310     defaultButton: null,
33311     buttonAlign: "right",
33312     tabTag: 'div',
33313     firstShow: true,
33314
33315     /**
33316      * Sets the dialog title text
33317      * @param {String} text The title text to display
33318      * @return {Roo.BasicDialog} this
33319      */
33320     setTitle : function(text){
33321         this.header.update(text);
33322         return this;
33323     },
33324
33325     // private
33326     closeClick : function(){
33327         this.hide();
33328     },
33329
33330     // private
33331     collapseClick : function(){
33332         this[this.collapsed ? "expand" : "collapse"]();
33333     },
33334
33335     /**
33336      * Collapses the dialog to its minimized state (only the title bar is visible).
33337      * Equivalent to the user clicking the collapse dialog button.
33338      */
33339     collapse : function(){
33340         if(!this.collapsed){
33341             this.collapsed = true;
33342             this.el.addClass("x-dlg-collapsed");
33343             this.restoreHeight = this.el.getHeight();
33344             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33345         }
33346     },
33347
33348     /**
33349      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33350      * clicking the expand dialog button.
33351      */
33352     expand : function(){
33353         if(this.collapsed){
33354             this.collapsed = false;
33355             this.el.removeClass("x-dlg-collapsed");
33356             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33357         }
33358     },
33359
33360     /**
33361      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33362      * @return {Roo.TabPanel} The tabs component
33363      */
33364     initTabs : function(){
33365         var tabs = this.getTabs();
33366         while(tabs.getTab(0)){
33367             tabs.removeTab(0);
33368         }
33369         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33370             var dom = el.dom;
33371             tabs.addTab(Roo.id(dom), dom.title);
33372             dom.title = "";
33373         });
33374         tabs.activate(0);
33375         return tabs;
33376     },
33377
33378     // private
33379     beforeResize : function(){
33380         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33381     },
33382
33383     // private
33384     onResize : function(){
33385         this.refreshSize();
33386         this.syncBodyHeight();
33387         this.adjustAssets();
33388         this.focus();
33389         this.fireEvent("resize", this, this.size.width, this.size.height);
33390     },
33391
33392     // private
33393     onKeyDown : function(e){
33394         if(this.isVisible()){
33395             this.fireEvent("keydown", this, e);
33396         }
33397     },
33398
33399     /**
33400      * Resizes the dialog.
33401      * @param {Number} width
33402      * @param {Number} height
33403      * @return {Roo.BasicDialog} this
33404      */
33405     resizeTo : function(width, height){
33406         this.el.setSize(width, height);
33407         this.size = {width: width, height: height};
33408         this.syncBodyHeight();
33409         if(this.fixedcenter){
33410             this.center();
33411         }
33412         if(this.isVisible()){
33413             this.constrainXY();
33414             this.adjustAssets();
33415         }
33416         this.fireEvent("resize", this, width, height);
33417         return this;
33418     },
33419
33420
33421     /**
33422      * Resizes the dialog to fit the specified content size.
33423      * @param {Number} width
33424      * @param {Number} height
33425      * @return {Roo.BasicDialog} this
33426      */
33427     setContentSize : function(w, h){
33428         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33429         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33430         //if(!this.el.isBorderBox()){
33431             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33432             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33433         //}
33434         if(this.tabs){
33435             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33436             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33437         }
33438         this.resizeTo(w, h);
33439         return this;
33440     },
33441
33442     /**
33443      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33444      * executed in response to a particular key being pressed while the dialog is active.
33445      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33446      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33447      * @param {Function} fn The function to call
33448      * @param {Object} scope (optional) The scope of the function
33449      * @return {Roo.BasicDialog} this
33450      */
33451     addKeyListener : function(key, fn, scope){
33452         var keyCode, shift, ctrl, alt;
33453         if(typeof key == "object" && !(key instanceof Array)){
33454             keyCode = key["key"];
33455             shift = key["shift"];
33456             ctrl = key["ctrl"];
33457             alt = key["alt"];
33458         }else{
33459             keyCode = key;
33460         }
33461         var handler = function(dlg, e){
33462             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33463                 var k = e.getKey();
33464                 if(keyCode instanceof Array){
33465                     for(var i = 0, len = keyCode.length; i < len; i++){
33466                         if(keyCode[i] == k){
33467                           fn.call(scope || window, dlg, k, e);
33468                           return;
33469                         }
33470                     }
33471                 }else{
33472                     if(k == keyCode){
33473                         fn.call(scope || window, dlg, k, e);
33474                     }
33475                 }
33476             }
33477         };
33478         this.on("keydown", handler);
33479         return this;
33480     },
33481
33482     /**
33483      * Returns the TabPanel component (creates it if it doesn't exist).
33484      * Note: If you wish to simply check for the existence of tabs without creating them,
33485      * check for a null 'tabs' property.
33486      * @return {Roo.TabPanel} The tabs component
33487      */
33488     getTabs : function(){
33489         if(!this.tabs){
33490             this.el.addClass("x-dlg-auto-tabs");
33491             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33492             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33493         }
33494         return this.tabs;
33495     },
33496
33497     /**
33498      * Adds a button to the footer section of the dialog.
33499      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33500      * object or a valid Roo.DomHelper element config
33501      * @param {Function} handler The function called when the button is clicked
33502      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33503      * @return {Roo.Button} The new button
33504      */
33505     addButton : function(config, handler, scope){
33506         var dh = Roo.DomHelper;
33507         if(!this.footer){
33508             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33509         }
33510         if(!this.btnContainer){
33511             var tb = this.footer.createChild({
33512
33513                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33514                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33515             }, null, true);
33516             this.btnContainer = tb.firstChild.firstChild.firstChild;
33517         }
33518         var bconfig = {
33519             handler: handler,
33520             scope: scope,
33521             minWidth: this.minButtonWidth,
33522             hideParent:true
33523         };
33524         if(typeof config == "string"){
33525             bconfig.text = config;
33526         }else{
33527             if(config.tag){
33528                 bconfig.dhconfig = config;
33529             }else{
33530                 Roo.apply(bconfig, config);
33531             }
33532         }
33533         var fc = false;
33534         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33535             bconfig.position = Math.max(0, bconfig.position);
33536             fc = this.btnContainer.childNodes[bconfig.position];
33537         }
33538          
33539         var btn = new Roo.Button(
33540             fc ? 
33541                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33542                 : this.btnContainer.appendChild(document.createElement("td")),
33543             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33544             bconfig
33545         );
33546         this.syncBodyHeight();
33547         if(!this.buttons){
33548             /**
33549              * Array of all the buttons that have been added to this dialog via addButton
33550              * @type Array
33551              */
33552             this.buttons = [];
33553         }
33554         this.buttons.push(btn);
33555         return btn;
33556     },
33557
33558     /**
33559      * Sets the default button to be focused when the dialog is displayed.
33560      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33561      * @return {Roo.BasicDialog} this
33562      */
33563     setDefaultButton : function(btn){
33564         this.defaultButton = btn;
33565         return this;
33566     },
33567
33568     // private
33569     getHeaderFooterHeight : function(safe){
33570         var height = 0;
33571         if(this.header){
33572            height += this.header.getHeight();
33573         }
33574         if(this.footer){
33575            var fm = this.footer.getMargins();
33576             height += (this.footer.getHeight()+fm.top+fm.bottom);
33577         }
33578         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33579         height += this.centerBg.getPadding("tb");
33580         return height;
33581     },
33582
33583     // private
33584     syncBodyHeight : function()
33585     {
33586         var bd = this.body, // the text
33587             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33588             bw = this.bwrap;
33589         var height = this.size.height - this.getHeaderFooterHeight(false);
33590         bd.setHeight(height-bd.getMargins("tb"));
33591         var hh = this.header.getHeight();
33592         var h = this.size.height-hh;
33593         cb.setHeight(h);
33594         
33595         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33596         bw.setHeight(h-cb.getPadding("tb"));
33597         
33598         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33599         bd.setWidth(bw.getWidth(true));
33600         if(this.tabs){
33601             this.tabs.syncHeight();
33602             if(Roo.isIE){
33603                 this.tabs.el.repaint();
33604             }
33605         }
33606     },
33607
33608     /**
33609      * Restores the previous state of the dialog if Roo.state is configured.
33610      * @return {Roo.BasicDialog} this
33611      */
33612     restoreState : function(){
33613         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33614         if(box && box.width){
33615             this.xy = [box.x, box.y];
33616             this.resizeTo(box.width, box.height);
33617         }
33618         return this;
33619     },
33620
33621     // private
33622     beforeShow : function(){
33623         this.expand();
33624         if(this.fixedcenter){
33625             this.xy = this.el.getCenterXY(true);
33626         }
33627         if(this.modal){
33628             Roo.get(document.body).addClass("x-body-masked");
33629             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33630             this.mask.show();
33631         }
33632         this.constrainXY();
33633     },
33634
33635     // private
33636     animShow : function(){
33637         var b = Roo.get(this.animateTarget).getBox();
33638         this.proxy.setSize(b.width, b.height);
33639         this.proxy.setLocation(b.x, b.y);
33640         this.proxy.show();
33641         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33642                     true, .35, this.showEl.createDelegate(this));
33643     },
33644
33645     /**
33646      * Shows the dialog.
33647      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33648      * @return {Roo.BasicDialog} this
33649      */
33650     show : function(animateTarget){
33651         if (this.fireEvent("beforeshow", this) === false){
33652             return;
33653         }
33654         if(this.syncHeightBeforeShow){
33655             this.syncBodyHeight();
33656         }else if(this.firstShow){
33657             this.firstShow = false;
33658             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33659         }
33660         this.animateTarget = animateTarget || this.animateTarget;
33661         if(!this.el.isVisible()){
33662             this.beforeShow();
33663             if(this.animateTarget && Roo.get(this.animateTarget)){
33664                 this.animShow();
33665             }else{
33666                 this.showEl();
33667             }
33668         }
33669         return this;
33670     },
33671
33672     // private
33673     showEl : function(){
33674         this.proxy.hide();
33675         this.el.setXY(this.xy);
33676         this.el.show();
33677         this.adjustAssets(true);
33678         this.toFront();
33679         this.focus();
33680         // IE peekaboo bug - fix found by Dave Fenwick
33681         if(Roo.isIE){
33682             this.el.repaint();
33683         }
33684         this.fireEvent("show", this);
33685     },
33686
33687     /**
33688      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33689      * dialog itself will receive focus.
33690      */
33691     focus : function(){
33692         if(this.defaultButton){
33693             this.defaultButton.focus();
33694         }else{
33695             this.focusEl.focus();
33696         }
33697     },
33698
33699     // private
33700     constrainXY : function(){
33701         if(this.constraintoviewport !== false){
33702             if(!this.viewSize){
33703                 if(this.container){
33704                     var s = this.container.getSize();
33705                     this.viewSize = [s.width, s.height];
33706                 }else{
33707                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33708                 }
33709             }
33710             var s = Roo.get(this.container||document).getScroll();
33711
33712             var x = this.xy[0], y = this.xy[1];
33713             var w = this.size.width, h = this.size.height;
33714             var vw = this.viewSize[0], vh = this.viewSize[1];
33715             // only move it if it needs it
33716             var moved = false;
33717             // first validate right/bottom
33718             if(x + w > vw+s.left){
33719                 x = vw - w;
33720                 moved = true;
33721             }
33722             if(y + h > vh+s.top){
33723                 y = vh - h;
33724                 moved = true;
33725             }
33726             // then make sure top/left isn't negative
33727             if(x < s.left){
33728                 x = s.left;
33729                 moved = true;
33730             }
33731             if(y < s.top){
33732                 y = s.top;
33733                 moved = true;
33734             }
33735             if(moved){
33736                 // cache xy
33737                 this.xy = [x, y];
33738                 if(this.isVisible()){
33739                     this.el.setLocation(x, y);
33740                     this.adjustAssets();
33741                 }
33742             }
33743         }
33744     },
33745
33746     // private
33747     onDrag : function(){
33748         if(!this.proxyDrag){
33749             this.xy = this.el.getXY();
33750             this.adjustAssets();
33751         }
33752     },
33753
33754     // private
33755     adjustAssets : function(doShow){
33756         var x = this.xy[0], y = this.xy[1];
33757         var w = this.size.width, h = this.size.height;
33758         if(doShow === true){
33759             if(this.shadow){
33760                 this.shadow.show(this.el);
33761             }
33762             if(this.shim){
33763                 this.shim.show();
33764             }
33765         }
33766         if(this.shadow && this.shadow.isVisible()){
33767             this.shadow.show(this.el);
33768         }
33769         if(this.shim && this.shim.isVisible()){
33770             this.shim.setBounds(x, y, w, h);
33771         }
33772     },
33773
33774     // private
33775     adjustViewport : function(w, h){
33776         if(!w || !h){
33777             w = Roo.lib.Dom.getViewWidth();
33778             h = Roo.lib.Dom.getViewHeight();
33779         }
33780         // cache the size
33781         this.viewSize = [w, h];
33782         if(this.modal && this.mask.isVisible()){
33783             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33784             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33785         }
33786         if(this.isVisible()){
33787             this.constrainXY();
33788         }
33789     },
33790
33791     /**
33792      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33793      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33794      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33795      */
33796     destroy : function(removeEl){
33797         if(this.isVisible()){
33798             this.animateTarget = null;
33799             this.hide();
33800         }
33801         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33802         if(this.tabs){
33803             this.tabs.destroy(removeEl);
33804         }
33805         Roo.destroy(
33806              this.shim,
33807              this.proxy,
33808              this.resizer,
33809              this.close,
33810              this.mask
33811         );
33812         if(this.dd){
33813             this.dd.unreg();
33814         }
33815         if(this.buttons){
33816            for(var i = 0, len = this.buttons.length; i < len; i++){
33817                this.buttons[i].destroy();
33818            }
33819         }
33820         this.el.removeAllListeners();
33821         if(removeEl === true){
33822             this.el.update("");
33823             this.el.remove();
33824         }
33825         Roo.DialogManager.unregister(this);
33826     },
33827
33828     // private
33829     startMove : function(){
33830         if(this.proxyDrag){
33831             this.proxy.show();
33832         }
33833         if(this.constraintoviewport !== false){
33834             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33835         }
33836     },
33837
33838     // private
33839     endMove : function(){
33840         if(!this.proxyDrag){
33841             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33842         }else{
33843             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33844             this.proxy.hide();
33845         }
33846         this.refreshSize();
33847         this.adjustAssets();
33848         this.focus();
33849         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33850     },
33851
33852     /**
33853      * Brings this dialog to the front of any other visible dialogs
33854      * @return {Roo.BasicDialog} this
33855      */
33856     toFront : function(){
33857         Roo.DialogManager.bringToFront(this);
33858         return this;
33859     },
33860
33861     /**
33862      * Sends this dialog to the back (under) of any other visible dialogs
33863      * @return {Roo.BasicDialog} this
33864      */
33865     toBack : function(){
33866         Roo.DialogManager.sendToBack(this);
33867         return this;
33868     },
33869
33870     /**
33871      * Centers this dialog in the viewport
33872      * @return {Roo.BasicDialog} this
33873      */
33874     center : function(){
33875         var xy = this.el.getCenterXY(true);
33876         this.moveTo(xy[0], xy[1]);
33877         return this;
33878     },
33879
33880     /**
33881      * Moves the dialog's top-left corner to the specified point
33882      * @param {Number} x
33883      * @param {Number} y
33884      * @return {Roo.BasicDialog} this
33885      */
33886     moveTo : function(x, y){
33887         this.xy = [x,y];
33888         if(this.isVisible()){
33889             this.el.setXY(this.xy);
33890             this.adjustAssets();
33891         }
33892         return this;
33893     },
33894
33895     /**
33896      * Aligns the dialog to the specified element
33897      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33898      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33899      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33900      * @return {Roo.BasicDialog} this
33901      */
33902     alignTo : function(element, position, offsets){
33903         this.xy = this.el.getAlignToXY(element, position, offsets);
33904         if(this.isVisible()){
33905             this.el.setXY(this.xy);
33906             this.adjustAssets();
33907         }
33908         return this;
33909     },
33910
33911     /**
33912      * Anchors an element to another element and realigns it when the window is resized.
33913      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33914      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33915      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33916      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33917      * is a number, it is used as the buffer delay (defaults to 50ms).
33918      * @return {Roo.BasicDialog} this
33919      */
33920     anchorTo : function(el, alignment, offsets, monitorScroll){
33921         var action = function(){
33922             this.alignTo(el, alignment, offsets);
33923         };
33924         Roo.EventManager.onWindowResize(action, this);
33925         var tm = typeof monitorScroll;
33926         if(tm != 'undefined'){
33927             Roo.EventManager.on(window, 'scroll', action, this,
33928                 {buffer: tm == 'number' ? monitorScroll : 50});
33929         }
33930         action.call(this);
33931         return this;
33932     },
33933
33934     /**
33935      * Returns true if the dialog is visible
33936      * @return {Boolean}
33937      */
33938     isVisible : function(){
33939         return this.el.isVisible();
33940     },
33941
33942     // private
33943     animHide : function(callback){
33944         var b = Roo.get(this.animateTarget).getBox();
33945         this.proxy.show();
33946         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33947         this.el.hide();
33948         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33949                     this.hideEl.createDelegate(this, [callback]));
33950     },
33951
33952     /**
33953      * Hides the dialog.
33954      * @param {Function} callback (optional) Function to call when the dialog is hidden
33955      * @return {Roo.BasicDialog} this
33956      */
33957     hide : function(callback){
33958         if (this.fireEvent("beforehide", this) === false){
33959             return;
33960         }
33961         if(this.shadow){
33962             this.shadow.hide();
33963         }
33964         if(this.shim) {
33965           this.shim.hide();
33966         }
33967         // sometimes animateTarget seems to get set.. causing problems...
33968         // this just double checks..
33969         if(this.animateTarget && Roo.get(this.animateTarget)) {
33970            this.animHide(callback);
33971         }else{
33972             this.el.hide();
33973             this.hideEl(callback);
33974         }
33975         return this;
33976     },
33977
33978     // private
33979     hideEl : function(callback){
33980         this.proxy.hide();
33981         if(this.modal){
33982             this.mask.hide();
33983             Roo.get(document.body).removeClass("x-body-masked");
33984         }
33985         this.fireEvent("hide", this);
33986         if(typeof callback == "function"){
33987             callback();
33988         }
33989     },
33990
33991     // private
33992     hideAction : function(){
33993         this.setLeft("-10000px");
33994         this.setTop("-10000px");
33995         this.setStyle("visibility", "hidden");
33996     },
33997
33998     // private
33999     refreshSize : function(){
34000         this.size = this.el.getSize();
34001         this.xy = this.el.getXY();
34002         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34003     },
34004
34005     // private
34006     // z-index is managed by the DialogManager and may be overwritten at any time
34007     setZIndex : function(index){
34008         if(this.modal){
34009             this.mask.setStyle("z-index", index);
34010         }
34011         if(this.shim){
34012             this.shim.setStyle("z-index", ++index);
34013         }
34014         if(this.shadow){
34015             this.shadow.setZIndex(++index);
34016         }
34017         this.el.setStyle("z-index", ++index);
34018         if(this.proxy){
34019             this.proxy.setStyle("z-index", ++index);
34020         }
34021         if(this.resizer){
34022             this.resizer.proxy.setStyle("z-index", ++index);
34023         }
34024
34025         this.lastZIndex = index;
34026     },
34027
34028     /**
34029      * Returns the element for this dialog
34030      * @return {Roo.Element} The underlying dialog Element
34031      */
34032     getEl : function(){
34033         return this.el;
34034     }
34035 });
34036
34037 /**
34038  * @class Roo.DialogManager
34039  * Provides global access to BasicDialogs that have been created and
34040  * support for z-indexing (layering) multiple open dialogs.
34041  */
34042 Roo.DialogManager = function(){
34043     var list = {};
34044     var accessList = [];
34045     var front = null;
34046
34047     // private
34048     var sortDialogs = function(d1, d2){
34049         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34050     };
34051
34052     // private
34053     var orderDialogs = function(){
34054         accessList.sort(sortDialogs);
34055         var seed = Roo.DialogManager.zseed;
34056         for(var i = 0, len = accessList.length; i < len; i++){
34057             var dlg = accessList[i];
34058             if(dlg){
34059                 dlg.setZIndex(seed + (i*10));
34060             }
34061         }
34062     };
34063
34064     return {
34065         /**
34066          * The starting z-index for BasicDialogs (defaults to 9000)
34067          * @type Number The z-index value
34068          */
34069         zseed : 9000,
34070
34071         // private
34072         register : function(dlg){
34073             list[dlg.id] = dlg;
34074             accessList.push(dlg);
34075         },
34076
34077         // private
34078         unregister : function(dlg){
34079             delete list[dlg.id];
34080             var i=0;
34081             var len=0;
34082             if(!accessList.indexOf){
34083                 for(  i = 0, len = accessList.length; i < len; i++){
34084                     if(accessList[i] == dlg){
34085                         accessList.splice(i, 1);
34086                         return;
34087                     }
34088                 }
34089             }else{
34090                  i = accessList.indexOf(dlg);
34091                 if(i != -1){
34092                     accessList.splice(i, 1);
34093                 }
34094             }
34095         },
34096
34097         /**
34098          * Gets a registered dialog by id
34099          * @param {String/Object} id The id of the dialog or a dialog
34100          * @return {Roo.BasicDialog} this
34101          */
34102         get : function(id){
34103             return typeof id == "object" ? id : list[id];
34104         },
34105
34106         /**
34107          * Brings the specified dialog to the front
34108          * @param {String/Object} dlg The id of the dialog or a dialog
34109          * @return {Roo.BasicDialog} this
34110          */
34111         bringToFront : function(dlg){
34112             dlg = this.get(dlg);
34113             if(dlg != front){
34114                 front = dlg;
34115                 dlg._lastAccess = new Date().getTime();
34116                 orderDialogs();
34117             }
34118             return dlg;
34119         },
34120
34121         /**
34122          * Sends the specified dialog to the back
34123          * @param {String/Object} dlg The id of the dialog or a dialog
34124          * @return {Roo.BasicDialog} this
34125          */
34126         sendToBack : function(dlg){
34127             dlg = this.get(dlg);
34128             dlg._lastAccess = -(new Date().getTime());
34129             orderDialogs();
34130             return dlg;
34131         },
34132
34133         /**
34134          * Hides all dialogs
34135          */
34136         hideAll : function(){
34137             for(var id in list){
34138                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34139                     list[id].hide();
34140                 }
34141             }
34142         }
34143     };
34144 }();
34145
34146 /**
34147  * @class Roo.LayoutDialog
34148  * @extends Roo.BasicDialog
34149  * @children Roo.ContentPanel
34150  * @parent builder none
34151  * Dialog which provides adjustments for working with a layout in a Dialog.
34152  * Add your necessary layout config options to the dialog's config.<br>
34153  * Example usage (including a nested layout):
34154  * <pre><code>
34155 if(!dialog){
34156     dialog = new Roo.LayoutDialog("download-dlg", {
34157         modal: true,
34158         width:600,
34159         height:450,
34160         shadow:true,
34161         minWidth:500,
34162         minHeight:350,
34163         autoTabs:true,
34164         proxyDrag:true,
34165         // layout config merges with the dialog config
34166         center:{
34167             tabPosition: "top",
34168             alwaysShowTabs: true
34169         }
34170     });
34171     dialog.addKeyListener(27, dialog.hide, dialog);
34172     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34173     dialog.addButton("Build It!", this.getDownload, this);
34174
34175     // we can even add nested layouts
34176     var innerLayout = new Roo.BorderLayout("dl-inner", {
34177         east: {
34178             initialSize: 200,
34179             autoScroll:true,
34180             split:true
34181         },
34182         center: {
34183             autoScroll:true
34184         }
34185     });
34186     innerLayout.beginUpdate();
34187     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34188     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34189     innerLayout.endUpdate(true);
34190
34191     var layout = dialog.getLayout();
34192     layout.beginUpdate();
34193     layout.add("center", new Roo.ContentPanel("standard-panel",
34194                         {title: "Download the Source", fitToFrame:true}));
34195     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34196                {title: "Build your own roo.js"}));
34197     layout.getRegion("center").showPanel(sp);
34198     layout.endUpdate();
34199 }
34200 </code></pre>
34201     * @constructor
34202     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34203     * @param {Object} config configuration options
34204   */
34205 Roo.LayoutDialog = function(el, cfg){
34206     
34207     var config=  cfg;
34208     if (typeof(cfg) == 'undefined') {
34209         config = Roo.apply({}, el);
34210         // not sure why we use documentElement here.. - it should always be body.
34211         // IE7 borks horribly if we use documentElement.
34212         // webkit also does not like documentElement - it creates a body element...
34213         el = Roo.get( document.body || document.documentElement ).createChild();
34214         //config.autoCreate = true;
34215     }
34216     
34217     
34218     config.autoTabs = false;
34219     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34220     this.body.setStyle({overflow:"hidden", position:"relative"});
34221     this.layout = new Roo.BorderLayout(this.body.dom, config);
34222     this.layout.monitorWindowResize = false;
34223     this.el.addClass("x-dlg-auto-layout");
34224     // fix case when center region overwrites center function
34225     this.center = Roo.BasicDialog.prototype.center;
34226     this.on("show", this.layout.layout, this.layout, true);
34227     if (config.items) {
34228         var xitems = config.items;
34229         delete config.items;
34230         Roo.each(xitems, this.addxtype, this);
34231     }
34232     
34233     
34234 };
34235 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34236     
34237     
34238     /**
34239      * @cfg {Roo.LayoutRegion} east  
34240      */
34241     /**
34242      * @cfg {Roo.LayoutRegion} west
34243      */
34244     /**
34245      * @cfg {Roo.LayoutRegion} south
34246      */
34247     /**
34248      * @cfg {Roo.LayoutRegion} north
34249      */
34250     /**
34251      * @cfg {Roo.LayoutRegion} center
34252      */
34253     /**
34254      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34255      */
34256     
34257     
34258     /**
34259      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34260      * @deprecated
34261      */
34262     endUpdate : function(){
34263         this.layout.endUpdate();
34264     },
34265
34266     /**
34267      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34268      *  @deprecated
34269      */
34270     beginUpdate : function(){
34271         this.layout.beginUpdate();
34272     },
34273
34274     /**
34275      * Get the BorderLayout for this dialog
34276      * @return {Roo.BorderLayout}
34277      */
34278     getLayout : function(){
34279         return this.layout;
34280     },
34281
34282     showEl : function(){
34283         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34284         if(Roo.isIE7){
34285             this.layout.layout();
34286         }
34287     },
34288
34289     // private
34290     // Use the syncHeightBeforeShow config option to control this automatically
34291     syncBodyHeight : function(){
34292         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34293         if(this.layout){this.layout.layout();}
34294     },
34295     
34296       /**
34297      * Add an xtype element (actually adds to the layout.)
34298      * @return {Object} xdata xtype object data.
34299      */
34300     
34301     addxtype : function(c) {
34302         return this.layout.addxtype(c);
34303     }
34304 });/*
34305  * Based on:
34306  * Ext JS Library 1.1.1
34307  * Copyright(c) 2006-2007, Ext JS, LLC.
34308  *
34309  * Originally Released Under LGPL - original licence link has changed is not relivant.
34310  *
34311  * Fork - LGPL
34312  * <script type="text/javascript">
34313  */
34314  
34315 /**
34316  * @class Roo.MessageBox
34317  * @static
34318  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34319  * Example usage:
34320  *<pre><code>
34321 // Basic alert:
34322 Roo.Msg.alert('Status', 'Changes saved successfully.');
34323
34324 // Prompt for user data:
34325 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34326     if (btn == 'ok'){
34327         // process text value...
34328     }
34329 });
34330
34331 // Show a dialog using config options:
34332 Roo.Msg.show({
34333    title:'Save Changes?',
34334    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34335    buttons: Roo.Msg.YESNOCANCEL,
34336    fn: processResult,
34337    animEl: 'elId'
34338 });
34339 </code></pre>
34340  * @static
34341  */
34342 Roo.MessageBox = function(){
34343     var dlg, opt, mask, waitTimer;
34344     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34345     var buttons, activeTextEl, bwidth;
34346
34347     // private
34348     var handleButton = function(button){
34349         dlg.hide();
34350         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34351     };
34352
34353     // private
34354     var handleHide = function(){
34355         if(opt && opt.cls){
34356             dlg.el.removeClass(opt.cls);
34357         }
34358         if(waitTimer){
34359             Roo.TaskMgr.stop(waitTimer);
34360             waitTimer = null;
34361         }
34362     };
34363
34364     // private
34365     var updateButtons = function(b){
34366         var width = 0;
34367         if(!b){
34368             buttons["ok"].hide();
34369             buttons["cancel"].hide();
34370             buttons["yes"].hide();
34371             buttons["no"].hide();
34372             dlg.footer.dom.style.display = 'none';
34373             return width;
34374         }
34375         dlg.footer.dom.style.display = '';
34376         for(var k in buttons){
34377             if(typeof buttons[k] != "function"){
34378                 if(b[k]){
34379                     buttons[k].show();
34380                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34381                     width += buttons[k].el.getWidth()+15;
34382                 }else{
34383                     buttons[k].hide();
34384                 }
34385             }
34386         }
34387         return width;
34388     };
34389
34390     // private
34391     var handleEsc = function(d, k, e){
34392         if(opt && opt.closable !== false){
34393             dlg.hide();
34394         }
34395         if(e){
34396             e.stopEvent();
34397         }
34398     };
34399
34400     return {
34401         /**
34402          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34403          * @return {Roo.BasicDialog} The BasicDialog element
34404          */
34405         getDialog : function(){
34406            if(!dlg){
34407                 dlg = new Roo.BasicDialog("x-msg-box", {
34408                     autoCreate : true,
34409                     shadow: true,
34410                     draggable: true,
34411                     resizable:false,
34412                     constraintoviewport:false,
34413                     fixedcenter:true,
34414                     collapsible : false,
34415                     shim:true,
34416                     modal: true,
34417                     width:400, height:100,
34418                     buttonAlign:"center",
34419                     closeClick : function(){
34420                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34421                             handleButton("no");
34422                         }else{
34423                             handleButton("cancel");
34424                         }
34425                     }
34426                 });
34427               
34428                 dlg.on("hide", handleHide);
34429                 mask = dlg.mask;
34430                 dlg.addKeyListener(27, handleEsc);
34431                 buttons = {};
34432                 var bt = this.buttonText;
34433                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34434                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34435                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34436                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34437                 bodyEl = dlg.body.createChild({
34438
34439                     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>'
34440                 });
34441                 msgEl = bodyEl.dom.firstChild;
34442                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34443                 textboxEl.enableDisplayMode();
34444                 textboxEl.addKeyListener([10,13], function(){
34445                     if(dlg.isVisible() && opt && opt.buttons){
34446                         if(opt.buttons.ok){
34447                             handleButton("ok");
34448                         }else if(opt.buttons.yes){
34449                             handleButton("yes");
34450                         }
34451                     }
34452                 });
34453                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34454                 textareaEl.enableDisplayMode();
34455                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34456                 progressEl.enableDisplayMode();
34457                 var pf = progressEl.dom.firstChild;
34458                 if (pf) {
34459                     pp = Roo.get(pf.firstChild);
34460                     pp.setHeight(pf.offsetHeight);
34461                 }
34462                 
34463             }
34464             return dlg;
34465         },
34466
34467         /**
34468          * Updates the message box body text
34469          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34470          * the XHTML-compliant non-breaking space character '&amp;#160;')
34471          * @return {Roo.MessageBox} This message box
34472          */
34473         updateText : function(text){
34474             if(!dlg.isVisible() && !opt.width){
34475                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34476             }
34477             msgEl.innerHTML = text || '&#160;';
34478       
34479             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34480             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34481             var w = Math.max(
34482                     Math.min(opt.width || cw , this.maxWidth), 
34483                     Math.max(opt.minWidth || this.minWidth, bwidth)
34484             );
34485             if(opt.prompt){
34486                 activeTextEl.setWidth(w);
34487             }
34488             if(dlg.isVisible()){
34489                 dlg.fixedcenter = false;
34490             }
34491             // to big, make it scroll. = But as usual stupid IE does not support
34492             // !important..
34493             
34494             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34495                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34496                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34497             } else {
34498                 bodyEl.dom.style.height = '';
34499                 bodyEl.dom.style.overflowY = '';
34500             }
34501             if (cw > w) {
34502                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34503             } else {
34504                 bodyEl.dom.style.overflowX = '';
34505             }
34506             
34507             dlg.setContentSize(w, bodyEl.getHeight());
34508             if(dlg.isVisible()){
34509                 dlg.fixedcenter = true;
34510             }
34511             return this;
34512         },
34513
34514         /**
34515          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34516          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34517          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34518          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34519          * @return {Roo.MessageBox} This message box
34520          */
34521         updateProgress : function(value, text){
34522             if(text){
34523                 this.updateText(text);
34524             }
34525             if (pp) { // weird bug on my firefox - for some reason this is not defined
34526                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34527             }
34528             return this;
34529         },        
34530
34531         /**
34532          * Returns true if the message box is currently displayed
34533          * @return {Boolean} True if the message box is visible, else false
34534          */
34535         isVisible : function(){
34536             return dlg && dlg.isVisible();  
34537         },
34538
34539         /**
34540          * Hides the message box if it is displayed
34541          */
34542         hide : function(){
34543             if(this.isVisible()){
34544                 dlg.hide();
34545             }  
34546         },
34547
34548         /**
34549          * Displays a new message box, or reinitializes an existing message box, based on the config options
34550          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34551          * The following config object properties are supported:
34552          * <pre>
34553 Property    Type             Description
34554 ----------  ---------------  ------------------------------------------------------------------------------------
34555 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34556                                    closes (defaults to undefined)
34557 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34558                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34559 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34560                                    progress and wait dialogs will ignore this property and always hide the
34561                                    close button as they can only be closed programmatically.
34562 cls               String           A custom CSS class to apply to the message box element
34563 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34564                                    displayed (defaults to 75)
34565 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34566                                    function will be btn (the name of the button that was clicked, if applicable,
34567                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34568                                    Progress and wait dialogs will ignore this option since they do not respond to
34569                                    user actions and can only be closed programmatically, so any required function
34570                                    should be called by the same code after it closes the dialog.
34571 icon              String           A CSS class that provides a background image to be used as an icon for
34572                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34573 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34574 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34575 modal             Boolean          False to allow user interaction with the page while the message box is
34576                                    displayed (defaults to true)
34577 msg               String           A string that will replace the existing message box body text (defaults
34578                                    to the XHTML-compliant non-breaking space character '&#160;')
34579 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34580 progress          Boolean          True to display a progress bar (defaults to false)
34581 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34582 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34583 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34584 title             String           The title text
34585 value             String           The string value to set into the active textbox element if displayed
34586 wait              Boolean          True to display a progress bar (defaults to false)
34587 width             Number           The width of the dialog in pixels
34588 </pre>
34589          *
34590          * Example usage:
34591          * <pre><code>
34592 Roo.Msg.show({
34593    title: 'Address',
34594    msg: 'Please enter your address:',
34595    width: 300,
34596    buttons: Roo.MessageBox.OKCANCEL,
34597    multiline: true,
34598    fn: saveAddress,
34599    animEl: 'addAddressBtn'
34600 });
34601 </code></pre>
34602          * @param {Object} config Configuration options
34603          * @return {Roo.MessageBox} This message box
34604          */
34605         show : function(options)
34606         {
34607             
34608             // this causes nightmares if you show one dialog after another
34609             // especially on callbacks..
34610              
34611             if(this.isVisible()){
34612                 
34613                 this.hide();
34614                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34615                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34616                 Roo.log("New Dialog Message:" +  options.msg )
34617                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34618                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34619                 
34620             }
34621             var d = this.getDialog();
34622             opt = options;
34623             d.setTitle(opt.title || "&#160;");
34624             d.close.setDisplayed(opt.closable !== false);
34625             activeTextEl = textboxEl;
34626             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34627             if(opt.prompt){
34628                 if(opt.multiline){
34629                     textboxEl.hide();
34630                     textareaEl.show();
34631                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34632                         opt.multiline : this.defaultTextHeight);
34633                     activeTextEl = textareaEl;
34634                 }else{
34635                     textboxEl.show();
34636                     textareaEl.hide();
34637                 }
34638             }else{
34639                 textboxEl.hide();
34640                 textareaEl.hide();
34641             }
34642             progressEl.setDisplayed(opt.progress === true);
34643             this.updateProgress(0);
34644             activeTextEl.dom.value = opt.value || "";
34645             if(opt.prompt){
34646                 dlg.setDefaultButton(activeTextEl);
34647             }else{
34648                 var bs = opt.buttons;
34649                 var db = null;
34650                 if(bs && bs.ok){
34651                     db = buttons["ok"];
34652                 }else if(bs && bs.yes){
34653                     db = buttons["yes"];
34654                 }
34655                 dlg.setDefaultButton(db);
34656             }
34657             bwidth = updateButtons(opt.buttons);
34658             this.updateText(opt.msg);
34659             if(opt.cls){
34660                 d.el.addClass(opt.cls);
34661             }
34662             d.proxyDrag = opt.proxyDrag === true;
34663             d.modal = opt.modal !== false;
34664             d.mask = opt.modal !== false ? mask : false;
34665             if(!d.isVisible()){
34666                 // force it to the end of the z-index stack so it gets a cursor in FF
34667                 document.body.appendChild(dlg.el.dom);
34668                 d.animateTarget = null;
34669                 d.show(options.animEl);
34670             }
34671             dlg.toFront();
34672             return this;
34673         },
34674
34675         /**
34676          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34677          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34678          * and closing the message box when the process is complete.
34679          * @param {String} title The title bar text
34680          * @param {String} msg The message box body text
34681          * @return {Roo.MessageBox} This message box
34682          */
34683         progress : function(title, msg){
34684             this.show({
34685                 title : title,
34686                 msg : msg,
34687                 buttons: false,
34688                 progress:true,
34689                 closable:false,
34690                 minWidth: this.minProgressWidth,
34691                 modal : true
34692             });
34693             return this;
34694         },
34695
34696         /**
34697          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34698          * If a callback function is passed it will be called after the user clicks the button, and the
34699          * id of the button that was clicked will be passed as the only parameter to the callback
34700          * (could also be the top-right close button).
34701          * @param {String} title The title bar text
34702          * @param {String} msg The message box body text
34703          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34704          * @param {Object} scope (optional) The scope of the callback function
34705          * @return {Roo.MessageBox} This message box
34706          */
34707         alert : function(title, msg, fn, scope){
34708             this.show({
34709                 title : title,
34710                 msg : msg,
34711                 buttons: this.OK,
34712                 fn: fn,
34713                 scope : scope,
34714                 modal : true
34715             });
34716             return this;
34717         },
34718
34719         /**
34720          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34721          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34722          * You are responsible for closing the message box when the process is complete.
34723          * @param {String} msg The message box body text
34724          * @param {String} title (optional) The title bar text
34725          * @return {Roo.MessageBox} This message box
34726          */
34727         wait : function(msg, title){
34728             this.show({
34729                 title : title,
34730                 msg : msg,
34731                 buttons: false,
34732                 closable:false,
34733                 progress:true,
34734                 modal:true,
34735                 width:300,
34736                 wait:true
34737             });
34738             waitTimer = Roo.TaskMgr.start({
34739                 run: function(i){
34740                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34741                 },
34742                 interval: 1000
34743             });
34744             return this;
34745         },
34746
34747         /**
34748          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34749          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34750          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34751          * @param {String} title The title bar text
34752          * @param {String} msg The message box body text
34753          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34754          * @param {Object} scope (optional) The scope of the callback function
34755          * @return {Roo.MessageBox} This message box
34756          */
34757         confirm : function(title, msg, fn, scope){
34758             this.show({
34759                 title : title,
34760                 msg : msg,
34761                 buttons: this.YESNO,
34762                 fn: fn,
34763                 scope : scope,
34764                 modal : true
34765             });
34766             return this;
34767         },
34768
34769         /**
34770          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34771          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34772          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34773          * (could also be the top-right close button) and the text that was entered will be passed as the two
34774          * parameters to the callback.
34775          * @param {String} title The title bar text
34776          * @param {String} msg The message box body text
34777          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34778          * @param {Object} scope (optional) The scope of the callback function
34779          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34780          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34781          * @return {Roo.MessageBox} This message box
34782          */
34783         prompt : function(title, msg, fn, scope, multiline){
34784             this.show({
34785                 title : title,
34786                 msg : msg,
34787                 buttons: this.OKCANCEL,
34788                 fn: fn,
34789                 minWidth:250,
34790                 scope : scope,
34791                 prompt:true,
34792                 multiline: multiline,
34793                 modal : true
34794             });
34795             return this;
34796         },
34797
34798         /**
34799          * Button config that displays a single OK button
34800          * @type Object
34801          */
34802         OK : {ok:true},
34803         /**
34804          * Button config that displays Yes and No buttons
34805          * @type Object
34806          */
34807         YESNO : {yes:true, no:true},
34808         /**
34809          * Button config that displays OK and Cancel buttons
34810          * @type Object
34811          */
34812         OKCANCEL : {ok:true, cancel:true},
34813         /**
34814          * Button config that displays Yes, No and Cancel buttons
34815          * @type Object
34816          */
34817         YESNOCANCEL : {yes:true, no:true, cancel:true},
34818
34819         /**
34820          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34821          * @type Number
34822          */
34823         defaultTextHeight : 75,
34824         /**
34825          * The maximum width in pixels of the message box (defaults to 600)
34826          * @type Number
34827          */
34828         maxWidth : 600,
34829         /**
34830          * The minimum width in pixels of the message box (defaults to 100)
34831          * @type Number
34832          */
34833         minWidth : 100,
34834         /**
34835          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34836          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34837          * @type Number
34838          */
34839         minProgressWidth : 250,
34840         /**
34841          * An object containing the default button text strings that can be overriden for localized language support.
34842          * Supported properties are: ok, cancel, yes and no.
34843          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34844          * @type Object
34845          */
34846         buttonText : {
34847             ok : "OK",
34848             cancel : "Cancel",
34849             yes : "Yes",
34850             no : "No"
34851         }
34852     };
34853 }();
34854
34855 /**
34856  * Shorthand for {@link Roo.MessageBox}
34857  */
34858 Roo.Msg = Roo.MessageBox;/*
34859  * Based on:
34860  * Ext JS Library 1.1.1
34861  * Copyright(c) 2006-2007, Ext JS, LLC.
34862  *
34863  * Originally Released Under LGPL - original licence link has changed is not relivant.
34864  *
34865  * Fork - LGPL
34866  * <script type="text/javascript">
34867  */
34868 /**
34869  * @class Roo.QuickTips
34870  * Provides attractive and customizable tooltips for any element.
34871  * @static
34872  */
34873 Roo.QuickTips = function(){
34874     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34875     var ce, bd, xy, dd;
34876     var visible = false, disabled = true, inited = false;
34877     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34878     
34879     var onOver = function(e){
34880         if(disabled){
34881             return;
34882         }
34883         var t = e.getTarget();
34884         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34885             return;
34886         }
34887         if(ce && t == ce.el){
34888             clearTimeout(hideProc);
34889             return;
34890         }
34891         if(t && tagEls[t.id]){
34892             tagEls[t.id].el = t;
34893             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34894             return;
34895         }
34896         var ttp, et = Roo.fly(t);
34897         var ns = cfg.namespace;
34898         if(tm.interceptTitles && t.title){
34899             ttp = t.title;
34900             t.qtip = ttp;
34901             t.removeAttribute("title");
34902             e.preventDefault();
34903         }else{
34904             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34905         }
34906         if(ttp){
34907             showProc = show.defer(tm.showDelay, tm, [{
34908                 el: t, 
34909                 text: ttp.replace(/\\n/g,'<br/>'),
34910                 width: et.getAttributeNS(ns, cfg.width),
34911                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34912                 title: et.getAttributeNS(ns, cfg.title),
34913                     cls: et.getAttributeNS(ns, cfg.cls)
34914             }]);
34915         }
34916     };
34917     
34918     var onOut = function(e){
34919         clearTimeout(showProc);
34920         var t = e.getTarget();
34921         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34922             hideProc = setTimeout(hide, tm.hideDelay);
34923         }
34924     };
34925     
34926     var onMove = function(e){
34927         if(disabled){
34928             return;
34929         }
34930         xy = e.getXY();
34931         xy[1] += 18;
34932         if(tm.trackMouse && ce){
34933             el.setXY(xy);
34934         }
34935     };
34936     
34937     var onDown = function(e){
34938         clearTimeout(showProc);
34939         clearTimeout(hideProc);
34940         if(!e.within(el)){
34941             if(tm.hideOnClick){
34942                 hide();
34943                 tm.disable();
34944                 tm.enable.defer(100, tm);
34945             }
34946         }
34947     };
34948     
34949     var getPad = function(){
34950         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34951     };
34952
34953     var show = function(o){
34954         if(disabled){
34955             return;
34956         }
34957         clearTimeout(dismissProc);
34958         ce = o;
34959         if(removeCls){ // in case manually hidden
34960             el.removeClass(removeCls);
34961             removeCls = null;
34962         }
34963         if(ce.cls){
34964             el.addClass(ce.cls);
34965             removeCls = ce.cls;
34966         }
34967         if(ce.title){
34968             tipTitle.update(ce.title);
34969             tipTitle.show();
34970         }else{
34971             tipTitle.update('');
34972             tipTitle.hide();
34973         }
34974         el.dom.style.width  = tm.maxWidth+'px';
34975         //tipBody.dom.style.width = '';
34976         tipBodyText.update(o.text);
34977         var p = getPad(), w = ce.width;
34978         if(!w){
34979             var td = tipBodyText.dom;
34980             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34981             if(aw > tm.maxWidth){
34982                 w = tm.maxWidth;
34983             }else if(aw < tm.minWidth){
34984                 w = tm.minWidth;
34985             }else{
34986                 w = aw;
34987             }
34988         }
34989         //tipBody.setWidth(w);
34990         el.setWidth(parseInt(w, 10) + p);
34991         if(ce.autoHide === false){
34992             close.setDisplayed(true);
34993             if(dd){
34994                 dd.unlock();
34995             }
34996         }else{
34997             close.setDisplayed(false);
34998             if(dd){
34999                 dd.lock();
35000             }
35001         }
35002         if(xy){
35003             el.avoidY = xy[1]-18;
35004             el.setXY(xy);
35005         }
35006         if(tm.animate){
35007             el.setOpacity(.1);
35008             el.setStyle("visibility", "visible");
35009             el.fadeIn({callback: afterShow});
35010         }else{
35011             afterShow();
35012         }
35013     };
35014     
35015     var afterShow = function(){
35016         if(ce){
35017             el.show();
35018             esc.enable();
35019             if(tm.autoDismiss && ce.autoHide !== false){
35020                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35021             }
35022         }
35023     };
35024     
35025     var hide = function(noanim){
35026         clearTimeout(dismissProc);
35027         clearTimeout(hideProc);
35028         ce = null;
35029         if(el.isVisible()){
35030             esc.disable();
35031             if(noanim !== true && tm.animate){
35032                 el.fadeOut({callback: afterHide});
35033             }else{
35034                 afterHide();
35035             } 
35036         }
35037     };
35038     
35039     var afterHide = function(){
35040         el.hide();
35041         if(removeCls){
35042             el.removeClass(removeCls);
35043             removeCls = null;
35044         }
35045     };
35046     
35047     return {
35048         /**
35049         * @cfg {Number} minWidth
35050         * The minimum width of the quick tip (defaults to 40)
35051         */
35052        minWidth : 40,
35053         /**
35054         * @cfg {Number} maxWidth
35055         * The maximum width of the quick tip (defaults to 300)
35056         */
35057        maxWidth : 300,
35058         /**
35059         * @cfg {Boolean} interceptTitles
35060         * True to automatically use the element's DOM title value if available (defaults to false)
35061         */
35062        interceptTitles : false,
35063         /**
35064         * @cfg {Boolean} trackMouse
35065         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35066         */
35067        trackMouse : false,
35068         /**
35069         * @cfg {Boolean} hideOnClick
35070         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35071         */
35072        hideOnClick : true,
35073         /**
35074         * @cfg {Number} showDelay
35075         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35076         */
35077        showDelay : 500,
35078         /**
35079         * @cfg {Number} hideDelay
35080         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35081         */
35082        hideDelay : 200,
35083         /**
35084         * @cfg {Boolean} autoHide
35085         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35086         * Used in conjunction with hideDelay.
35087         */
35088        autoHide : true,
35089         /**
35090         * @cfg {Boolean}
35091         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35092         * (defaults to true).  Used in conjunction with autoDismissDelay.
35093         */
35094        autoDismiss : true,
35095         /**
35096         * @cfg {Number}
35097         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35098         */
35099        autoDismissDelay : 5000,
35100        /**
35101         * @cfg {Boolean} animate
35102         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35103         */
35104        animate : false,
35105
35106        /**
35107         * @cfg {String} title
35108         * Title text to display (defaults to '').  This can be any valid HTML markup.
35109         */
35110         title: '',
35111        /**
35112         * @cfg {String} text
35113         * Body text to display (defaults to '').  This can be any valid HTML markup.
35114         */
35115         text : '',
35116        /**
35117         * @cfg {String} cls
35118         * A CSS class to apply to the base quick tip element (defaults to '').
35119         */
35120         cls : '',
35121        /**
35122         * @cfg {Number} width
35123         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35124         * minWidth or maxWidth.
35125         */
35126         width : null,
35127
35128     /**
35129      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35130      * or display QuickTips in a page.
35131      */
35132        init : function(){
35133           tm = Roo.QuickTips;
35134           cfg = tm.tagConfig;
35135           if(!inited){
35136               if(!Roo.isReady){ // allow calling of init() before onReady
35137                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35138                   return;
35139               }
35140               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35141               el.fxDefaults = {stopFx: true};
35142               // maximum custom styling
35143               //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>');
35144               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>');              
35145               tipTitle = el.child('h3');
35146               tipTitle.enableDisplayMode("block");
35147               tipBody = el.child('div.x-tip-bd');
35148               tipBodyText = el.child('div.x-tip-bd-inner');
35149               //bdLeft = el.child('div.x-tip-bd-left');
35150               //bdRight = el.child('div.x-tip-bd-right');
35151               close = el.child('div.x-tip-close');
35152               close.enableDisplayMode("block");
35153               close.on("click", hide);
35154               var d = Roo.get(document);
35155               d.on("mousedown", onDown);
35156               d.on("mouseover", onOver);
35157               d.on("mouseout", onOut);
35158               d.on("mousemove", onMove);
35159               esc = d.addKeyListener(27, hide);
35160               esc.disable();
35161               if(Roo.dd.DD){
35162                   dd = el.initDD("default", null, {
35163                       onDrag : function(){
35164                           el.sync();  
35165                       }
35166                   });
35167                   dd.setHandleElId(tipTitle.id);
35168                   dd.lock();
35169               }
35170               inited = true;
35171           }
35172           this.enable(); 
35173        },
35174
35175     /**
35176      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35177      * are supported:
35178      * <pre>
35179 Property    Type                   Description
35180 ----------  ---------------------  ------------------------------------------------------------------------
35181 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35182      * </ul>
35183      * @param {Object} config The config object
35184      */
35185        register : function(config){
35186            var cs = config instanceof Array ? config : arguments;
35187            for(var i = 0, len = cs.length; i < len; i++) {
35188                var c = cs[i];
35189                var target = c.target;
35190                if(target){
35191                    if(target instanceof Array){
35192                        for(var j = 0, jlen = target.length; j < jlen; j++){
35193                            tagEls[target[j]] = c;
35194                        }
35195                    }else{
35196                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35197                    }
35198                }
35199            }
35200        },
35201
35202     /**
35203      * Removes this quick tip from its element and destroys it.
35204      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35205      */
35206        unregister : function(el){
35207            delete tagEls[Roo.id(el)];
35208        },
35209
35210     /**
35211      * Enable this quick tip.
35212      */
35213        enable : function(){
35214            if(inited && disabled){
35215                locks.pop();
35216                if(locks.length < 1){
35217                    disabled = false;
35218                }
35219            }
35220        },
35221
35222     /**
35223      * Disable this quick tip.
35224      */
35225        disable : function(){
35226           disabled = true;
35227           clearTimeout(showProc);
35228           clearTimeout(hideProc);
35229           clearTimeout(dismissProc);
35230           if(ce){
35231               hide(true);
35232           }
35233           locks.push(1);
35234        },
35235
35236     /**
35237      * Returns true if the quick tip is enabled, else false.
35238      */
35239        isEnabled : function(){
35240             return !disabled;
35241        },
35242
35243         // private
35244        tagConfig : {
35245            namespace : "roo", // was ext?? this may break..
35246            alt_namespace : "ext",
35247            attribute : "qtip",
35248            width : "width",
35249            target : "target",
35250            title : "qtitle",
35251            hide : "hide",
35252            cls : "qclass"
35253        }
35254    };
35255 }();
35256
35257 // backwards compat
35258 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35259  * Based on:
35260  * Ext JS Library 1.1.1
35261  * Copyright(c) 2006-2007, Ext JS, LLC.
35262  *
35263  * Originally Released Under LGPL - original licence link has changed is not relivant.
35264  *
35265  * Fork - LGPL
35266  * <script type="text/javascript">
35267  */
35268  
35269
35270 /**
35271  * @class Roo.tree.TreePanel
35272  * @extends Roo.data.Tree
35273  * @cfg {Roo.tree.TreeNode} root The root node
35274  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35275  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35276  * @cfg {Boolean} enableDD true to enable drag and drop
35277  * @cfg {Boolean} enableDrag true to enable just drag
35278  * @cfg {Boolean} enableDrop true to enable just drop
35279  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35280  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35281  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35282  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35283  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35284  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35285  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35286  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35287  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35288  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35289  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35290  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35291  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35292  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35293  * @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>
35294  * @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>
35295  * 
35296  * @constructor
35297  * @param {String/HTMLElement/Element} el The container element
35298  * @param {Object} config
35299  */
35300 Roo.tree.TreePanel = function(el, config){
35301     var root = false;
35302     var loader = false;
35303     if (config.root) {
35304         root = config.root;
35305         delete config.root;
35306     }
35307     if (config.loader) {
35308         loader = config.loader;
35309         delete config.loader;
35310     }
35311     
35312     Roo.apply(this, config);
35313     Roo.tree.TreePanel.superclass.constructor.call(this);
35314     this.el = Roo.get(el);
35315     this.el.addClass('x-tree');
35316     //console.log(root);
35317     if (root) {
35318         this.setRootNode( Roo.factory(root, Roo.tree));
35319     }
35320     if (loader) {
35321         this.loader = Roo.factory(loader, Roo.tree);
35322     }
35323    /**
35324     * Read-only. The id of the container element becomes this TreePanel's id.
35325     */
35326     this.id = this.el.id;
35327     this.addEvents({
35328         /**
35329         * @event beforeload
35330         * Fires before a node is loaded, return false to cancel
35331         * @param {Node} node The node being loaded
35332         */
35333         "beforeload" : true,
35334         /**
35335         * @event load
35336         * Fires when a node is loaded
35337         * @param {Node} node The node that was loaded
35338         */
35339         "load" : true,
35340         /**
35341         * @event textchange
35342         * Fires when the text for a node is changed
35343         * @param {Node} node The node
35344         * @param {String} text The new text
35345         * @param {String} oldText The old text
35346         */
35347         "textchange" : true,
35348         /**
35349         * @event beforeexpand
35350         * Fires before a node is expanded, return false to cancel.
35351         * @param {Node} node The node
35352         * @param {Boolean} deep
35353         * @param {Boolean} anim
35354         */
35355         "beforeexpand" : true,
35356         /**
35357         * @event beforecollapse
35358         * Fires before a node is collapsed, return false to cancel.
35359         * @param {Node} node The node
35360         * @param {Boolean} deep
35361         * @param {Boolean} anim
35362         */
35363         "beforecollapse" : true,
35364         /**
35365         * @event expand
35366         * Fires when a node is expanded
35367         * @param {Node} node The node
35368         */
35369         "expand" : true,
35370         /**
35371         * @event disabledchange
35372         * Fires when the disabled status of a node changes
35373         * @param {Node} node The node
35374         * @param {Boolean} disabled
35375         */
35376         "disabledchange" : true,
35377         /**
35378         * @event collapse
35379         * Fires when a node is collapsed
35380         * @param {Node} node The node
35381         */
35382         "collapse" : true,
35383         /**
35384         * @event beforeclick
35385         * Fires before click processing on a node. Return false to cancel the default action.
35386         * @param {Node} node The node
35387         * @param {Roo.EventObject} e The event object
35388         */
35389         "beforeclick":true,
35390         /**
35391         * @event checkchange
35392         * Fires when a node with a checkbox's checked property changes
35393         * @param {Node} this This node
35394         * @param {Boolean} checked
35395         */
35396         "checkchange":true,
35397         /**
35398         * @event click
35399         * Fires when a node is clicked
35400         * @param {Node} node The node
35401         * @param {Roo.EventObject} e The event object
35402         */
35403         "click":true,
35404         /**
35405         * @event dblclick
35406         * Fires when a node is double clicked
35407         * @param {Node} node The node
35408         * @param {Roo.EventObject} e The event object
35409         */
35410         "dblclick":true,
35411         /**
35412         * @event contextmenu
35413         * Fires when a node is right clicked
35414         * @param {Node} node The node
35415         * @param {Roo.EventObject} e The event object
35416         */
35417         "contextmenu":true,
35418         /**
35419         * @event beforechildrenrendered
35420         * Fires right before the child nodes for a node are rendered
35421         * @param {Node} node The node
35422         */
35423         "beforechildrenrendered":true,
35424         /**
35425         * @event startdrag
35426         * Fires when a node starts being dragged
35427         * @param {Roo.tree.TreePanel} this
35428         * @param {Roo.tree.TreeNode} node
35429         * @param {event} e The raw browser event
35430         */ 
35431        "startdrag" : true,
35432        /**
35433         * @event enddrag
35434         * Fires when a drag operation is complete
35435         * @param {Roo.tree.TreePanel} this
35436         * @param {Roo.tree.TreeNode} node
35437         * @param {event} e The raw browser event
35438         */
35439        "enddrag" : true,
35440        /**
35441         * @event dragdrop
35442         * Fires when a dragged node is dropped on a valid DD target
35443         * @param {Roo.tree.TreePanel} this
35444         * @param {Roo.tree.TreeNode} node
35445         * @param {DD} dd The dd it was dropped on
35446         * @param {event} e The raw browser event
35447         */
35448        "dragdrop" : true,
35449        /**
35450         * @event beforenodedrop
35451         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35452         * passed to handlers has the following properties:<br />
35453         * <ul style="padding:5px;padding-left:16px;">
35454         * <li>tree - The TreePanel</li>
35455         * <li>target - The node being targeted for the drop</li>
35456         * <li>data - The drag data from the drag source</li>
35457         * <li>point - The point of the drop - append, above or below</li>
35458         * <li>source - The drag source</li>
35459         * <li>rawEvent - Raw mouse event</li>
35460         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35461         * to be inserted by setting them on this object.</li>
35462         * <li>cancel - Set this to true to cancel the drop.</li>
35463         * </ul>
35464         * @param {Object} dropEvent
35465         */
35466        "beforenodedrop" : true,
35467        /**
35468         * @event nodedrop
35469         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35470         * passed to handlers has the following properties:<br />
35471         * <ul style="padding:5px;padding-left:16px;">
35472         * <li>tree - The TreePanel</li>
35473         * <li>target - The node being targeted for the drop</li>
35474         * <li>data - The drag data from the drag source</li>
35475         * <li>point - The point of the drop - append, above or below</li>
35476         * <li>source - The drag source</li>
35477         * <li>rawEvent - Raw mouse event</li>
35478         * <li>dropNode - Dropped node(s).</li>
35479         * </ul>
35480         * @param {Object} dropEvent
35481         */
35482        "nodedrop" : true,
35483         /**
35484         * @event nodedragover
35485         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35486         * passed to handlers has the following properties:<br />
35487         * <ul style="padding:5px;padding-left:16px;">
35488         * <li>tree - The TreePanel</li>
35489         * <li>target - The node being targeted for the drop</li>
35490         * <li>data - The drag data from the drag source</li>
35491         * <li>point - The point of the drop - append, above or below</li>
35492         * <li>source - The drag source</li>
35493         * <li>rawEvent - Raw mouse event</li>
35494         * <li>dropNode - Drop node(s) provided by the source.</li>
35495         * <li>cancel - Set this to true to signal drop not allowed.</li>
35496         * </ul>
35497         * @param {Object} dragOverEvent
35498         */
35499        "nodedragover" : true,
35500        /**
35501         * @event appendnode
35502         * Fires when append node to the tree
35503         * @param {Roo.tree.TreePanel} this
35504         * @param {Roo.tree.TreeNode} node
35505         * @param {Number} index The index of the newly appended node
35506         */
35507        "appendnode" : true
35508         
35509     });
35510     if(this.singleExpand){
35511        this.on("beforeexpand", this.restrictExpand, this);
35512     }
35513     if (this.editor) {
35514         this.editor.tree = this;
35515         this.editor = Roo.factory(this.editor, Roo.tree);
35516     }
35517     
35518     if (this.selModel) {
35519         this.selModel = Roo.factory(this.selModel, Roo.tree);
35520     }
35521    
35522 };
35523 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35524     rootVisible : true,
35525     animate: Roo.enableFx,
35526     lines : true,
35527     enableDD : false,
35528     hlDrop : Roo.enableFx,
35529   
35530     renderer: false,
35531     
35532     rendererTip: false,
35533     // private
35534     restrictExpand : function(node){
35535         var p = node.parentNode;
35536         if(p){
35537             if(p.expandedChild && p.expandedChild.parentNode == p){
35538                 p.expandedChild.collapse();
35539             }
35540             p.expandedChild = node;
35541         }
35542     },
35543
35544     // private override
35545     setRootNode : function(node){
35546         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35547         if(!this.rootVisible){
35548             node.ui = new Roo.tree.RootTreeNodeUI(node);
35549         }
35550         return node;
35551     },
35552
35553     /**
35554      * Returns the container element for this TreePanel
35555      */
35556     getEl : function(){
35557         return this.el;
35558     },
35559
35560     /**
35561      * Returns the default TreeLoader for this TreePanel
35562      */
35563     getLoader : function(){
35564         return this.loader;
35565     },
35566
35567     /**
35568      * Expand all nodes
35569      */
35570     expandAll : function(){
35571         this.root.expand(true);
35572     },
35573
35574     /**
35575      * Collapse all nodes
35576      */
35577     collapseAll : function(){
35578         this.root.collapse(true);
35579     },
35580
35581     /**
35582      * Returns the selection model used by this TreePanel
35583      */
35584     getSelectionModel : function(){
35585         if(!this.selModel){
35586             this.selModel = new Roo.tree.DefaultSelectionModel();
35587         }
35588         return this.selModel;
35589     },
35590
35591     /**
35592      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35593      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35594      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35595      * @return {Array}
35596      */
35597     getChecked : function(a, startNode){
35598         startNode = startNode || this.root;
35599         var r = [];
35600         var f = function(){
35601             if(this.attributes.checked){
35602                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35603             }
35604         }
35605         startNode.cascade(f);
35606         return r;
35607     },
35608
35609     /**
35610      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35611      * @param {String} path
35612      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35613      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35614      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35615      */
35616     expandPath : function(path, attr, callback){
35617         attr = attr || "id";
35618         var keys = path.split(this.pathSeparator);
35619         var curNode = this.root;
35620         if(curNode.attributes[attr] != keys[1]){ // invalid root
35621             if(callback){
35622                 callback(false, null);
35623             }
35624             return;
35625         }
35626         var index = 1;
35627         var f = function(){
35628             if(++index == keys.length){
35629                 if(callback){
35630                     callback(true, curNode);
35631                 }
35632                 return;
35633             }
35634             var c = curNode.findChild(attr, keys[index]);
35635             if(!c){
35636                 if(callback){
35637                     callback(false, curNode);
35638                 }
35639                 return;
35640             }
35641             curNode = c;
35642             c.expand(false, false, f);
35643         };
35644         curNode.expand(false, false, f);
35645     },
35646
35647     /**
35648      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35649      * @param {String} path
35650      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35651      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35652      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35653      */
35654     selectPath : function(path, attr, callback){
35655         attr = attr || "id";
35656         var keys = path.split(this.pathSeparator);
35657         var v = keys.pop();
35658         if(keys.length > 0){
35659             var f = function(success, node){
35660                 if(success && node){
35661                     var n = node.findChild(attr, v);
35662                     if(n){
35663                         n.select();
35664                         if(callback){
35665                             callback(true, n);
35666                         }
35667                     }else if(callback){
35668                         callback(false, n);
35669                     }
35670                 }else{
35671                     if(callback){
35672                         callback(false, n);
35673                     }
35674                 }
35675             };
35676             this.expandPath(keys.join(this.pathSeparator), attr, f);
35677         }else{
35678             this.root.select();
35679             if(callback){
35680                 callback(true, this.root);
35681             }
35682         }
35683     },
35684
35685     getTreeEl : function(){
35686         return this.el;
35687     },
35688
35689     /**
35690      * Trigger rendering of this TreePanel
35691      */
35692     render : function(){
35693         if (this.innerCt) {
35694             return this; // stop it rendering more than once!!
35695         }
35696         
35697         this.innerCt = this.el.createChild({tag:"ul",
35698                cls:"x-tree-root-ct " +
35699                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35700
35701         if(this.containerScroll){
35702             Roo.dd.ScrollManager.register(this.el);
35703         }
35704         if((this.enableDD || this.enableDrop) && !this.dropZone){
35705            /**
35706             * The dropZone used by this tree if drop is enabled
35707             * @type Roo.tree.TreeDropZone
35708             */
35709              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35710                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35711            });
35712         }
35713         if((this.enableDD || this.enableDrag) && !this.dragZone){
35714            /**
35715             * The dragZone used by this tree if drag is enabled
35716             * @type Roo.tree.TreeDragZone
35717             */
35718             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35719                ddGroup: this.ddGroup || "TreeDD",
35720                scroll: this.ddScroll
35721            });
35722         }
35723         this.getSelectionModel().init(this);
35724         if (!this.root) {
35725             Roo.log("ROOT not set in tree");
35726             return this;
35727         }
35728         this.root.render();
35729         if(!this.rootVisible){
35730             this.root.renderChildren();
35731         }
35732         return this;
35733     }
35734 });/*
35735  * Based on:
35736  * Ext JS Library 1.1.1
35737  * Copyright(c) 2006-2007, Ext JS, LLC.
35738  *
35739  * Originally Released Under LGPL - original licence link has changed is not relivant.
35740  *
35741  * Fork - LGPL
35742  * <script type="text/javascript">
35743  */
35744  
35745
35746 /**
35747  * @class Roo.tree.DefaultSelectionModel
35748  * @extends Roo.util.Observable
35749  * The default single selection for a TreePanel.
35750  * @param {Object} cfg Configuration
35751  */
35752 Roo.tree.DefaultSelectionModel = function(cfg){
35753    this.selNode = null;
35754    
35755    
35756    
35757    this.addEvents({
35758        /**
35759         * @event selectionchange
35760         * Fires when the selected node changes
35761         * @param {DefaultSelectionModel} this
35762         * @param {TreeNode} node the new selection
35763         */
35764        "selectionchange" : true,
35765
35766        /**
35767         * @event beforeselect
35768         * Fires before the selected node changes, return false to cancel the change
35769         * @param {DefaultSelectionModel} this
35770         * @param {TreeNode} node the new selection
35771         * @param {TreeNode} node the old selection
35772         */
35773        "beforeselect" : true
35774    });
35775    
35776     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35777 };
35778
35779 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35780     init : function(tree){
35781         this.tree = tree;
35782         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35783         tree.on("click", this.onNodeClick, this);
35784     },
35785     
35786     onNodeClick : function(node, e){
35787         if (e.ctrlKey && this.selNode == node)  {
35788             this.unselect(node);
35789             return;
35790         }
35791         this.select(node);
35792     },
35793     
35794     /**
35795      * Select a node.
35796      * @param {TreeNode} node The node to select
35797      * @return {TreeNode} The selected node
35798      */
35799     select : function(node){
35800         var last = this.selNode;
35801         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35802             if(last){
35803                 last.ui.onSelectedChange(false);
35804             }
35805             this.selNode = node;
35806             node.ui.onSelectedChange(true);
35807             this.fireEvent("selectionchange", this, node, last);
35808         }
35809         return node;
35810     },
35811     
35812     /**
35813      * Deselect a node.
35814      * @param {TreeNode} node The node to unselect
35815      */
35816     unselect : function(node){
35817         if(this.selNode == node){
35818             this.clearSelections();
35819         }    
35820     },
35821     
35822     /**
35823      * Clear all selections
35824      */
35825     clearSelections : function(){
35826         var n = this.selNode;
35827         if(n){
35828             n.ui.onSelectedChange(false);
35829             this.selNode = null;
35830             this.fireEvent("selectionchange", this, null);
35831         }
35832         return n;
35833     },
35834     
35835     /**
35836      * Get the selected node
35837      * @return {TreeNode} The selected node
35838      */
35839     getSelectedNode : function(){
35840         return this.selNode;    
35841     },
35842     
35843     /**
35844      * Returns true if the node is selected
35845      * @param {TreeNode} node The node to check
35846      * @return {Boolean}
35847      */
35848     isSelected : function(node){
35849         return this.selNode == node;  
35850     },
35851
35852     /**
35853      * Selects the node above the selected node in the tree, intelligently walking the nodes
35854      * @return TreeNode The new selection
35855      */
35856     selectPrevious : function(){
35857         var s = this.selNode || this.lastSelNode;
35858         if(!s){
35859             return null;
35860         }
35861         var ps = s.previousSibling;
35862         if(ps){
35863             if(!ps.isExpanded() || ps.childNodes.length < 1){
35864                 return this.select(ps);
35865             } else{
35866                 var lc = ps.lastChild;
35867                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35868                     lc = lc.lastChild;
35869                 }
35870                 return this.select(lc);
35871             }
35872         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35873             return this.select(s.parentNode);
35874         }
35875         return null;
35876     },
35877
35878     /**
35879      * Selects the node above the selected node in the tree, intelligently walking the nodes
35880      * @return TreeNode The new selection
35881      */
35882     selectNext : function(){
35883         var s = this.selNode || this.lastSelNode;
35884         if(!s){
35885             return null;
35886         }
35887         if(s.firstChild && s.isExpanded()){
35888              return this.select(s.firstChild);
35889          }else if(s.nextSibling){
35890              return this.select(s.nextSibling);
35891          }else if(s.parentNode){
35892             var newS = null;
35893             s.parentNode.bubble(function(){
35894                 if(this.nextSibling){
35895                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35896                     return false;
35897                 }
35898             });
35899             return newS;
35900          }
35901         return null;
35902     },
35903
35904     onKeyDown : function(e){
35905         var s = this.selNode || this.lastSelNode;
35906         // undesirable, but required
35907         var sm = this;
35908         if(!s){
35909             return;
35910         }
35911         var k = e.getKey();
35912         switch(k){
35913              case e.DOWN:
35914                  e.stopEvent();
35915                  this.selectNext();
35916              break;
35917              case e.UP:
35918                  e.stopEvent();
35919                  this.selectPrevious();
35920              break;
35921              case e.RIGHT:
35922                  e.preventDefault();
35923                  if(s.hasChildNodes()){
35924                      if(!s.isExpanded()){
35925                          s.expand();
35926                      }else if(s.firstChild){
35927                          this.select(s.firstChild, e);
35928                      }
35929                  }
35930              break;
35931              case e.LEFT:
35932                  e.preventDefault();
35933                  if(s.hasChildNodes() && s.isExpanded()){
35934                      s.collapse();
35935                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35936                      this.select(s.parentNode, e);
35937                  }
35938              break;
35939         };
35940     }
35941 });
35942
35943 /**
35944  * @class Roo.tree.MultiSelectionModel
35945  * @extends Roo.util.Observable
35946  * Multi selection for a TreePanel.
35947  * @param {Object} cfg Configuration
35948  */
35949 Roo.tree.MultiSelectionModel = function(){
35950    this.selNodes = [];
35951    this.selMap = {};
35952    this.addEvents({
35953        /**
35954         * @event selectionchange
35955         * Fires when the selected nodes change
35956         * @param {MultiSelectionModel} this
35957         * @param {Array} nodes Array of the selected nodes
35958         */
35959        "selectionchange" : true
35960    });
35961    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35962    
35963 };
35964
35965 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35966     init : function(tree){
35967         this.tree = tree;
35968         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35969         tree.on("click", this.onNodeClick, this);
35970     },
35971     
35972     onNodeClick : function(node, e){
35973         this.select(node, e, e.ctrlKey);
35974     },
35975     
35976     /**
35977      * Select a node.
35978      * @param {TreeNode} node The node to select
35979      * @param {EventObject} e (optional) An event associated with the selection
35980      * @param {Boolean} keepExisting True to retain existing selections
35981      * @return {TreeNode} The selected node
35982      */
35983     select : function(node, e, keepExisting){
35984         if(keepExisting !== true){
35985             this.clearSelections(true);
35986         }
35987         if(this.isSelected(node)){
35988             this.lastSelNode = node;
35989             return node;
35990         }
35991         this.selNodes.push(node);
35992         this.selMap[node.id] = node;
35993         this.lastSelNode = node;
35994         node.ui.onSelectedChange(true);
35995         this.fireEvent("selectionchange", this, this.selNodes);
35996         return node;
35997     },
35998     
35999     /**
36000      * Deselect a node.
36001      * @param {TreeNode} node The node to unselect
36002      */
36003     unselect : function(node){
36004         if(this.selMap[node.id]){
36005             node.ui.onSelectedChange(false);
36006             var sn = this.selNodes;
36007             var index = -1;
36008             if(sn.indexOf){
36009                 index = sn.indexOf(node);
36010             }else{
36011                 for(var i = 0, len = sn.length; i < len; i++){
36012                     if(sn[i] == node){
36013                         index = i;
36014                         break;
36015                     }
36016                 }
36017             }
36018             if(index != -1){
36019                 this.selNodes.splice(index, 1);
36020             }
36021             delete this.selMap[node.id];
36022             this.fireEvent("selectionchange", this, this.selNodes);
36023         }
36024     },
36025     
36026     /**
36027      * Clear all selections
36028      */
36029     clearSelections : function(suppressEvent){
36030         var sn = this.selNodes;
36031         if(sn.length > 0){
36032             for(var i = 0, len = sn.length; i < len; i++){
36033                 sn[i].ui.onSelectedChange(false);
36034             }
36035             this.selNodes = [];
36036             this.selMap = {};
36037             if(suppressEvent !== true){
36038                 this.fireEvent("selectionchange", this, this.selNodes);
36039             }
36040         }
36041     },
36042     
36043     /**
36044      * Returns true if the node is selected
36045      * @param {TreeNode} node The node to check
36046      * @return {Boolean}
36047      */
36048     isSelected : function(node){
36049         return this.selMap[node.id] ? true : false;  
36050     },
36051     
36052     /**
36053      * Returns an array of the selected nodes
36054      * @return {Array}
36055      */
36056     getSelectedNodes : function(){
36057         return this.selNodes;    
36058     },
36059
36060     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36061
36062     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36063
36064     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36065 });/*
36066  * Based on:
36067  * Ext JS Library 1.1.1
36068  * Copyright(c) 2006-2007, Ext JS, LLC.
36069  *
36070  * Originally Released Under LGPL - original licence link has changed is not relivant.
36071  *
36072  * Fork - LGPL
36073  * <script type="text/javascript">
36074  */
36075  
36076 /**
36077  * @class Roo.tree.TreeNode
36078  * @extends Roo.data.Node
36079  * @cfg {String} text The text for this node
36080  * @cfg {Boolean} expanded true to start the node expanded
36081  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36082  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36083  * @cfg {Boolean} disabled true to start the node disabled
36084  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36085  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36086  * @cfg {String} cls A css class to be added to the node
36087  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36088  * @cfg {String} href URL of the link used for the node (defaults to #)
36089  * @cfg {String} hrefTarget target frame for the link
36090  * @cfg {String} qtip An Ext QuickTip for the node
36091  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36092  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36093  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36094  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36095  * (defaults to undefined with no checkbox rendered)
36096  * @constructor
36097  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36098  */
36099 Roo.tree.TreeNode = function(attributes){
36100     attributes = attributes || {};
36101     if(typeof attributes == "string"){
36102         attributes = {text: attributes};
36103     }
36104     this.childrenRendered = false;
36105     this.rendered = false;
36106     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36107     this.expanded = attributes.expanded === true;
36108     this.isTarget = attributes.isTarget !== false;
36109     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36110     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36111
36112     /**
36113      * Read-only. The text for this node. To change it use setText().
36114      * @type String
36115      */
36116     this.text = attributes.text;
36117     /**
36118      * True if this node is disabled.
36119      * @type Boolean
36120      */
36121     this.disabled = attributes.disabled === true;
36122
36123     this.addEvents({
36124         /**
36125         * @event textchange
36126         * Fires when the text for this node is changed
36127         * @param {Node} this This node
36128         * @param {String} text The new text
36129         * @param {String} oldText The old text
36130         */
36131         "textchange" : true,
36132         /**
36133         * @event beforeexpand
36134         * Fires before this node is expanded, return false to cancel.
36135         * @param {Node} this This node
36136         * @param {Boolean} deep
36137         * @param {Boolean} anim
36138         */
36139         "beforeexpand" : true,
36140         /**
36141         * @event beforecollapse
36142         * Fires before this node is collapsed, return false to cancel.
36143         * @param {Node} this This node
36144         * @param {Boolean} deep
36145         * @param {Boolean} anim
36146         */
36147         "beforecollapse" : true,
36148         /**
36149         * @event expand
36150         * Fires when this node is expanded
36151         * @param {Node} this This node
36152         */
36153         "expand" : true,
36154         /**
36155         * @event disabledchange
36156         * Fires when the disabled status of this node changes
36157         * @param {Node} this This node
36158         * @param {Boolean} disabled
36159         */
36160         "disabledchange" : true,
36161         /**
36162         * @event collapse
36163         * Fires when this node is collapsed
36164         * @param {Node} this This node
36165         */
36166         "collapse" : true,
36167         /**
36168         * @event beforeclick
36169         * Fires before click processing. Return false to cancel the default action.
36170         * @param {Node} this This node
36171         * @param {Roo.EventObject} e The event object
36172         */
36173         "beforeclick":true,
36174         /**
36175         * @event checkchange
36176         * Fires when a node with a checkbox's checked property changes
36177         * @param {Node} this This node
36178         * @param {Boolean} checked
36179         */
36180         "checkchange":true,
36181         /**
36182         * @event click
36183         * Fires when this node is clicked
36184         * @param {Node} this This node
36185         * @param {Roo.EventObject} e The event object
36186         */
36187         "click":true,
36188         /**
36189         * @event dblclick
36190         * Fires when this node is double clicked
36191         * @param {Node} this This node
36192         * @param {Roo.EventObject} e The event object
36193         */
36194         "dblclick":true,
36195         /**
36196         * @event contextmenu
36197         * Fires when this node is right clicked
36198         * @param {Node} this This node
36199         * @param {Roo.EventObject} e The event object
36200         */
36201         "contextmenu":true,
36202         /**
36203         * @event beforechildrenrendered
36204         * Fires right before the child nodes for this node are rendered
36205         * @param {Node} this This node
36206         */
36207         "beforechildrenrendered":true
36208     });
36209
36210     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36211
36212     /**
36213      * Read-only. The UI for this node
36214      * @type TreeNodeUI
36215      */
36216     this.ui = new uiClass(this);
36217     
36218     // finally support items[]
36219     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36220         return;
36221     }
36222     
36223     
36224     Roo.each(this.attributes.items, function(c) {
36225         this.appendChild(Roo.factory(c,Roo.Tree));
36226     }, this);
36227     delete this.attributes.items;
36228     
36229     
36230     
36231 };
36232 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36233     preventHScroll: true,
36234     /**
36235      * Returns true if this node is expanded
36236      * @return {Boolean}
36237      */
36238     isExpanded : function(){
36239         return this.expanded;
36240     },
36241
36242     /**
36243      * Returns the UI object for this node
36244      * @return {TreeNodeUI}
36245      */
36246     getUI : function(){
36247         return this.ui;
36248     },
36249
36250     // private override
36251     setFirstChild : function(node){
36252         var of = this.firstChild;
36253         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36254         if(this.childrenRendered && of && node != of){
36255             of.renderIndent(true, true);
36256         }
36257         if(this.rendered){
36258             this.renderIndent(true, true);
36259         }
36260     },
36261
36262     // private override
36263     setLastChild : function(node){
36264         var ol = this.lastChild;
36265         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36266         if(this.childrenRendered && ol && node != ol){
36267             ol.renderIndent(true, true);
36268         }
36269         if(this.rendered){
36270             this.renderIndent(true, true);
36271         }
36272     },
36273
36274     // these methods are overridden to provide lazy rendering support
36275     // private override
36276     appendChild : function()
36277     {
36278         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36279         if(node && this.childrenRendered){
36280             node.render();
36281         }
36282         this.ui.updateExpandIcon();
36283         return node;
36284     },
36285
36286     // private override
36287     removeChild : function(node){
36288         this.ownerTree.getSelectionModel().unselect(node);
36289         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36290         // if it's been rendered remove dom node
36291         if(this.childrenRendered){
36292             node.ui.remove();
36293         }
36294         if(this.childNodes.length < 1){
36295             this.collapse(false, false);
36296         }else{
36297             this.ui.updateExpandIcon();
36298         }
36299         if(!this.firstChild) {
36300             this.childrenRendered = false;
36301         }
36302         return node;
36303     },
36304
36305     // private override
36306     insertBefore : function(node, refNode){
36307         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36308         if(newNode && refNode && this.childrenRendered){
36309             node.render();
36310         }
36311         this.ui.updateExpandIcon();
36312         return newNode;
36313     },
36314
36315     /**
36316      * Sets the text for this node
36317      * @param {String} text
36318      */
36319     setText : function(text){
36320         var oldText = this.text;
36321         this.text = text;
36322         this.attributes.text = text;
36323         if(this.rendered){ // event without subscribing
36324             this.ui.onTextChange(this, text, oldText);
36325         }
36326         this.fireEvent("textchange", this, text, oldText);
36327     },
36328
36329     /**
36330      * Triggers selection of this node
36331      */
36332     select : function(){
36333         this.getOwnerTree().getSelectionModel().select(this);
36334     },
36335
36336     /**
36337      * Triggers deselection of this node
36338      */
36339     unselect : function(){
36340         this.getOwnerTree().getSelectionModel().unselect(this);
36341     },
36342
36343     /**
36344      * Returns true if this node is selected
36345      * @return {Boolean}
36346      */
36347     isSelected : function(){
36348         return this.getOwnerTree().getSelectionModel().isSelected(this);
36349     },
36350
36351     /**
36352      * Expand this node.
36353      * @param {Boolean} deep (optional) True to expand all children as well
36354      * @param {Boolean} anim (optional) false to cancel the default animation
36355      * @param {Function} callback (optional) A callback to be called when
36356      * expanding this node completes (does not wait for deep expand to complete).
36357      * Called with 1 parameter, this node.
36358      */
36359     expand : function(deep, anim, callback){
36360         if(!this.expanded){
36361             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36362                 return;
36363             }
36364             if(!this.childrenRendered){
36365                 this.renderChildren();
36366             }
36367             this.expanded = true;
36368             
36369             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36370                 this.ui.animExpand(function(){
36371                     this.fireEvent("expand", this);
36372                     if(typeof callback == "function"){
36373                         callback(this);
36374                     }
36375                     if(deep === true){
36376                         this.expandChildNodes(true);
36377                     }
36378                 }.createDelegate(this));
36379                 return;
36380             }else{
36381                 this.ui.expand();
36382                 this.fireEvent("expand", this);
36383                 if(typeof callback == "function"){
36384                     callback(this);
36385                 }
36386             }
36387         }else{
36388            if(typeof callback == "function"){
36389                callback(this);
36390            }
36391         }
36392         if(deep === true){
36393             this.expandChildNodes(true);
36394         }
36395     },
36396
36397     isHiddenRoot : function(){
36398         return this.isRoot && !this.getOwnerTree().rootVisible;
36399     },
36400
36401     /**
36402      * Collapse this node.
36403      * @param {Boolean} deep (optional) True to collapse all children as well
36404      * @param {Boolean} anim (optional) false to cancel the default animation
36405      */
36406     collapse : function(deep, anim){
36407         if(this.expanded && !this.isHiddenRoot()){
36408             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36409                 return;
36410             }
36411             this.expanded = false;
36412             if((this.getOwnerTree().animate && anim !== false) || anim){
36413                 this.ui.animCollapse(function(){
36414                     this.fireEvent("collapse", this);
36415                     if(deep === true){
36416                         this.collapseChildNodes(true);
36417                     }
36418                 }.createDelegate(this));
36419                 return;
36420             }else{
36421                 this.ui.collapse();
36422                 this.fireEvent("collapse", this);
36423             }
36424         }
36425         if(deep === true){
36426             var cs = this.childNodes;
36427             for(var i = 0, len = cs.length; i < len; i++) {
36428                 cs[i].collapse(true, false);
36429             }
36430         }
36431     },
36432
36433     // private
36434     delayedExpand : function(delay){
36435         if(!this.expandProcId){
36436             this.expandProcId = this.expand.defer(delay, this);
36437         }
36438     },
36439
36440     // private
36441     cancelExpand : function(){
36442         if(this.expandProcId){
36443             clearTimeout(this.expandProcId);
36444         }
36445         this.expandProcId = false;
36446     },
36447
36448     /**
36449      * Toggles expanded/collapsed state of the node
36450      */
36451     toggle : function(){
36452         if(this.expanded){
36453             this.collapse();
36454         }else{
36455             this.expand();
36456         }
36457     },
36458
36459     /**
36460      * Ensures all parent nodes are expanded
36461      */
36462     ensureVisible : function(callback){
36463         var tree = this.getOwnerTree();
36464         tree.expandPath(this.parentNode.getPath(), false, function(){
36465             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36466             Roo.callback(callback);
36467         }.createDelegate(this));
36468     },
36469
36470     /**
36471      * Expand all child nodes
36472      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36473      */
36474     expandChildNodes : function(deep){
36475         var cs = this.childNodes;
36476         for(var i = 0, len = cs.length; i < len; i++) {
36477                 cs[i].expand(deep);
36478         }
36479     },
36480
36481     /**
36482      * Collapse all child nodes
36483      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36484      */
36485     collapseChildNodes : function(deep){
36486         var cs = this.childNodes;
36487         for(var i = 0, len = cs.length; i < len; i++) {
36488                 cs[i].collapse(deep);
36489         }
36490     },
36491
36492     /**
36493      * Disables this node
36494      */
36495     disable : function(){
36496         this.disabled = true;
36497         this.unselect();
36498         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36499             this.ui.onDisableChange(this, true);
36500         }
36501         this.fireEvent("disabledchange", this, true);
36502     },
36503
36504     /**
36505      * Enables this node
36506      */
36507     enable : function(){
36508         this.disabled = false;
36509         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36510             this.ui.onDisableChange(this, false);
36511         }
36512         this.fireEvent("disabledchange", this, false);
36513     },
36514
36515     // private
36516     renderChildren : function(suppressEvent){
36517         if(suppressEvent !== false){
36518             this.fireEvent("beforechildrenrendered", this);
36519         }
36520         var cs = this.childNodes;
36521         for(var i = 0, len = cs.length; i < len; i++){
36522             cs[i].render(true);
36523         }
36524         this.childrenRendered = true;
36525     },
36526
36527     // private
36528     sort : function(fn, scope){
36529         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36530         if(this.childrenRendered){
36531             var cs = this.childNodes;
36532             for(var i = 0, len = cs.length; i < len; i++){
36533                 cs[i].render(true);
36534             }
36535         }
36536     },
36537
36538     // private
36539     render : function(bulkRender){
36540         this.ui.render(bulkRender);
36541         if(!this.rendered){
36542             this.rendered = true;
36543             if(this.expanded){
36544                 this.expanded = false;
36545                 this.expand(false, false);
36546             }
36547         }
36548     },
36549
36550     // private
36551     renderIndent : function(deep, refresh){
36552         if(refresh){
36553             this.ui.childIndent = null;
36554         }
36555         this.ui.renderIndent();
36556         if(deep === true && this.childrenRendered){
36557             var cs = this.childNodes;
36558             for(var i = 0, len = cs.length; i < len; i++){
36559                 cs[i].renderIndent(true, refresh);
36560             }
36561         }
36562     }
36563 });/*
36564  * Based on:
36565  * Ext JS Library 1.1.1
36566  * Copyright(c) 2006-2007, Ext JS, LLC.
36567  *
36568  * Originally Released Under LGPL - original licence link has changed is not relivant.
36569  *
36570  * Fork - LGPL
36571  * <script type="text/javascript">
36572  */
36573  
36574 /**
36575  * @class Roo.tree.AsyncTreeNode
36576  * @extends Roo.tree.TreeNode
36577  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36578  * @constructor
36579  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36580  */
36581  Roo.tree.AsyncTreeNode = function(config){
36582     this.loaded = false;
36583     this.loading = false;
36584     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36585     /**
36586     * @event beforeload
36587     * Fires before this node is loaded, return false to cancel
36588     * @param {Node} this This node
36589     */
36590     this.addEvents({'beforeload':true, 'load': true});
36591     /**
36592     * @event load
36593     * Fires when this node is loaded
36594     * @param {Node} this This node
36595     */
36596     /**
36597      * The loader used by this node (defaults to using the tree's defined loader)
36598      * @type TreeLoader
36599      * @property loader
36600      */
36601 };
36602 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36603     expand : function(deep, anim, callback){
36604         if(this.loading){ // if an async load is already running, waiting til it's done
36605             var timer;
36606             var f = function(){
36607                 if(!this.loading){ // done loading
36608                     clearInterval(timer);
36609                     this.expand(deep, anim, callback);
36610                 }
36611             }.createDelegate(this);
36612             timer = setInterval(f, 200);
36613             return;
36614         }
36615         if(!this.loaded){
36616             if(this.fireEvent("beforeload", this) === false){
36617                 return;
36618             }
36619             this.loading = true;
36620             this.ui.beforeLoad(this);
36621             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36622             if(loader){
36623                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36624                 return;
36625             }
36626         }
36627         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36628     },
36629     
36630     /**
36631      * Returns true if this node is currently loading
36632      * @return {Boolean}
36633      */
36634     isLoading : function(){
36635         return this.loading;  
36636     },
36637     
36638     loadComplete : function(deep, anim, callback){
36639         this.loading = false;
36640         this.loaded = true;
36641         this.ui.afterLoad(this);
36642         this.fireEvent("load", this);
36643         this.expand(deep, anim, callback);
36644     },
36645     
36646     /**
36647      * Returns true if this node has been loaded
36648      * @return {Boolean}
36649      */
36650     isLoaded : function(){
36651         return this.loaded;
36652     },
36653     
36654     hasChildNodes : function(){
36655         if(!this.isLeaf() && !this.loaded){
36656             return true;
36657         }else{
36658             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36659         }
36660     },
36661
36662     /**
36663      * Trigger a reload for this node
36664      * @param {Function} callback
36665      */
36666     reload : function(callback){
36667         this.collapse(false, false);
36668         while(this.firstChild){
36669             this.removeChild(this.firstChild);
36670         }
36671         this.childrenRendered = false;
36672         this.loaded = false;
36673         if(this.isHiddenRoot()){
36674             this.expanded = false;
36675         }
36676         this.expand(false, false, callback);
36677     }
36678 });/*
36679  * Based on:
36680  * Ext JS Library 1.1.1
36681  * Copyright(c) 2006-2007, Ext JS, LLC.
36682  *
36683  * Originally Released Under LGPL - original licence link has changed is not relivant.
36684  *
36685  * Fork - LGPL
36686  * <script type="text/javascript">
36687  */
36688  
36689 /**
36690  * @class Roo.tree.TreeNodeUI
36691  * @constructor
36692  * @param {Object} node The node to render
36693  * The TreeNode UI implementation is separate from the
36694  * tree implementation. Unless you are customizing the tree UI,
36695  * you should never have to use this directly.
36696  */
36697 Roo.tree.TreeNodeUI = function(node){
36698     this.node = node;
36699     this.rendered = false;
36700     this.animating = false;
36701     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36702 };
36703
36704 Roo.tree.TreeNodeUI.prototype = {
36705     removeChild : function(node){
36706         if(this.rendered){
36707             this.ctNode.removeChild(node.ui.getEl());
36708         }
36709     },
36710
36711     beforeLoad : function(){
36712          this.addClass("x-tree-node-loading");
36713     },
36714
36715     afterLoad : function(){
36716          this.removeClass("x-tree-node-loading");
36717     },
36718
36719     onTextChange : function(node, text, oldText){
36720         if(this.rendered){
36721             this.textNode.innerHTML = text;
36722         }
36723     },
36724
36725     onDisableChange : function(node, state){
36726         this.disabled = state;
36727         if(state){
36728             this.addClass("x-tree-node-disabled");
36729         }else{
36730             this.removeClass("x-tree-node-disabled");
36731         }
36732     },
36733
36734     onSelectedChange : function(state){
36735         if(state){
36736             this.focus();
36737             this.addClass("x-tree-selected");
36738         }else{
36739             //this.blur();
36740             this.removeClass("x-tree-selected");
36741         }
36742     },
36743
36744     onMove : function(tree, node, oldParent, newParent, index, refNode){
36745         this.childIndent = null;
36746         if(this.rendered){
36747             var targetNode = newParent.ui.getContainer();
36748             if(!targetNode){//target not rendered
36749                 this.holder = document.createElement("div");
36750                 this.holder.appendChild(this.wrap);
36751                 return;
36752             }
36753             var insertBefore = refNode ? refNode.ui.getEl() : null;
36754             if(insertBefore){
36755                 targetNode.insertBefore(this.wrap, insertBefore);
36756             }else{
36757                 targetNode.appendChild(this.wrap);
36758             }
36759             this.node.renderIndent(true);
36760         }
36761     },
36762
36763     addClass : function(cls){
36764         if(this.elNode){
36765             Roo.fly(this.elNode).addClass(cls);
36766         }
36767     },
36768
36769     removeClass : function(cls){
36770         if(this.elNode){
36771             Roo.fly(this.elNode).removeClass(cls);
36772         }
36773     },
36774
36775     remove : function(){
36776         if(this.rendered){
36777             this.holder = document.createElement("div");
36778             this.holder.appendChild(this.wrap);
36779         }
36780     },
36781
36782     fireEvent : function(){
36783         return this.node.fireEvent.apply(this.node, arguments);
36784     },
36785
36786     initEvents : function(){
36787         this.node.on("move", this.onMove, this);
36788         var E = Roo.EventManager;
36789         var a = this.anchor;
36790
36791         var el = Roo.fly(a, '_treeui');
36792
36793         if(Roo.isOpera){ // opera render bug ignores the CSS
36794             el.setStyle("text-decoration", "none");
36795         }
36796
36797         el.on("click", this.onClick, this);
36798         el.on("dblclick", this.onDblClick, this);
36799
36800         if(this.checkbox){
36801             Roo.EventManager.on(this.checkbox,
36802                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36803         }
36804
36805         el.on("contextmenu", this.onContextMenu, this);
36806
36807         var icon = Roo.fly(this.iconNode);
36808         icon.on("click", this.onClick, this);
36809         icon.on("dblclick", this.onDblClick, this);
36810         icon.on("contextmenu", this.onContextMenu, this);
36811         E.on(this.ecNode, "click", this.ecClick, this, true);
36812
36813         if(this.node.disabled){
36814             this.addClass("x-tree-node-disabled");
36815         }
36816         if(this.node.hidden){
36817             this.addClass("x-tree-node-disabled");
36818         }
36819         var ot = this.node.getOwnerTree();
36820         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36821         if(dd && (!this.node.isRoot || ot.rootVisible)){
36822             Roo.dd.Registry.register(this.elNode, {
36823                 node: this.node,
36824                 handles: this.getDDHandles(),
36825                 isHandle: false
36826             });
36827         }
36828     },
36829
36830     getDDHandles : function(){
36831         return [this.iconNode, this.textNode];
36832     },
36833
36834     hide : function(){
36835         if(this.rendered){
36836             this.wrap.style.display = "none";
36837         }
36838     },
36839
36840     show : function(){
36841         if(this.rendered){
36842             this.wrap.style.display = "";
36843         }
36844     },
36845
36846     onContextMenu : function(e){
36847         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36848             e.preventDefault();
36849             this.focus();
36850             this.fireEvent("contextmenu", this.node, e);
36851         }
36852     },
36853
36854     onClick : function(e){
36855         if(this.dropping){
36856             e.stopEvent();
36857             return;
36858         }
36859         if(this.fireEvent("beforeclick", this.node, e) !== false){
36860             if(!this.disabled && this.node.attributes.href){
36861                 this.fireEvent("click", this.node, e);
36862                 return;
36863             }
36864             e.preventDefault();
36865             if(this.disabled){
36866                 return;
36867             }
36868
36869             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36870                 this.node.toggle();
36871             }
36872
36873             this.fireEvent("click", this.node, e);
36874         }else{
36875             e.stopEvent();
36876         }
36877     },
36878
36879     onDblClick : function(e){
36880         e.preventDefault();
36881         if(this.disabled){
36882             return;
36883         }
36884         if(this.checkbox){
36885             this.toggleCheck();
36886         }
36887         if(!this.animating && this.node.hasChildNodes()){
36888             this.node.toggle();
36889         }
36890         this.fireEvent("dblclick", this.node, e);
36891     },
36892
36893     onCheckChange : function(){
36894         var checked = this.checkbox.checked;
36895         this.node.attributes.checked = checked;
36896         this.fireEvent('checkchange', this.node, checked);
36897     },
36898
36899     ecClick : function(e){
36900         if(!this.animating && this.node.hasChildNodes()){
36901             this.node.toggle();
36902         }
36903     },
36904
36905     startDrop : function(){
36906         this.dropping = true;
36907     },
36908
36909     // delayed drop so the click event doesn't get fired on a drop
36910     endDrop : function(){
36911        setTimeout(function(){
36912            this.dropping = false;
36913        }.createDelegate(this), 50);
36914     },
36915
36916     expand : function(){
36917         this.updateExpandIcon();
36918         this.ctNode.style.display = "";
36919     },
36920
36921     focus : function(){
36922         if(!this.node.preventHScroll){
36923             try{this.anchor.focus();
36924             }catch(e){}
36925         }else if(!Roo.isIE){
36926             try{
36927                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36928                 var l = noscroll.scrollLeft;
36929                 this.anchor.focus();
36930                 noscroll.scrollLeft = l;
36931             }catch(e){}
36932         }
36933     },
36934
36935     toggleCheck : function(value){
36936         var cb = this.checkbox;
36937         if(cb){
36938             cb.checked = (value === undefined ? !cb.checked : value);
36939         }
36940     },
36941
36942     blur : function(){
36943         try{
36944             this.anchor.blur();
36945         }catch(e){}
36946     },
36947
36948     animExpand : function(callback){
36949         var ct = Roo.get(this.ctNode);
36950         ct.stopFx();
36951         if(!this.node.hasChildNodes()){
36952             this.updateExpandIcon();
36953             this.ctNode.style.display = "";
36954             Roo.callback(callback);
36955             return;
36956         }
36957         this.animating = true;
36958         this.updateExpandIcon();
36959
36960         ct.slideIn('t', {
36961            callback : function(){
36962                this.animating = false;
36963                Roo.callback(callback);
36964             },
36965             scope: this,
36966             duration: this.node.ownerTree.duration || .25
36967         });
36968     },
36969
36970     highlight : function(){
36971         var tree = this.node.getOwnerTree();
36972         Roo.fly(this.wrap).highlight(
36973             tree.hlColor || "C3DAF9",
36974             {endColor: tree.hlBaseColor}
36975         );
36976     },
36977
36978     collapse : function(){
36979         this.updateExpandIcon();
36980         this.ctNode.style.display = "none";
36981     },
36982
36983     animCollapse : function(callback){
36984         var ct = Roo.get(this.ctNode);
36985         ct.enableDisplayMode('block');
36986         ct.stopFx();
36987
36988         this.animating = true;
36989         this.updateExpandIcon();
36990
36991         ct.slideOut('t', {
36992             callback : function(){
36993                this.animating = false;
36994                Roo.callback(callback);
36995             },
36996             scope: this,
36997             duration: this.node.ownerTree.duration || .25
36998         });
36999     },
37000
37001     getContainer : function(){
37002         return this.ctNode;
37003     },
37004
37005     getEl : function(){
37006         return this.wrap;
37007     },
37008
37009     appendDDGhost : function(ghostNode){
37010         ghostNode.appendChild(this.elNode.cloneNode(true));
37011     },
37012
37013     getDDRepairXY : function(){
37014         return Roo.lib.Dom.getXY(this.iconNode);
37015     },
37016
37017     onRender : function(){
37018         this.render();
37019     },
37020
37021     render : function(bulkRender){
37022         var n = this.node, a = n.attributes;
37023         var targetNode = n.parentNode ?
37024               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37025
37026         if(!this.rendered){
37027             this.rendered = true;
37028
37029             this.renderElements(n, a, targetNode, bulkRender);
37030
37031             if(a.qtip){
37032                if(this.textNode.setAttributeNS){
37033                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37034                    if(a.qtipTitle){
37035                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37036                    }
37037                }else{
37038                    this.textNode.setAttribute("ext:qtip", a.qtip);
37039                    if(a.qtipTitle){
37040                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37041                    }
37042                }
37043             }else if(a.qtipCfg){
37044                 a.qtipCfg.target = Roo.id(this.textNode);
37045                 Roo.QuickTips.register(a.qtipCfg);
37046             }
37047             this.initEvents();
37048             if(!this.node.expanded){
37049                 this.updateExpandIcon();
37050             }
37051         }else{
37052             if(bulkRender === true) {
37053                 targetNode.appendChild(this.wrap);
37054             }
37055         }
37056     },
37057
37058     renderElements : function(n, a, targetNode, bulkRender)
37059     {
37060         // add some indent caching, this helps performance when rendering a large tree
37061         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37062         var t = n.getOwnerTree();
37063         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37064         if (typeof(n.attributes.html) != 'undefined') {
37065             txt = n.attributes.html;
37066         }
37067         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37068         var cb = typeof a.checked == 'boolean';
37069         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37070         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37071             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37072             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37073             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37074             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37075             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37076              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37077                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37078             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37079             "</li>"];
37080
37081         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37082             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37083                                 n.nextSibling.ui.getEl(), buf.join(""));
37084         }else{
37085             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37086         }
37087
37088         this.elNode = this.wrap.childNodes[0];
37089         this.ctNode = this.wrap.childNodes[1];
37090         var cs = this.elNode.childNodes;
37091         this.indentNode = cs[0];
37092         this.ecNode = cs[1];
37093         this.iconNode = cs[2];
37094         var index = 3;
37095         if(cb){
37096             this.checkbox = cs[3];
37097             index++;
37098         }
37099         this.anchor = cs[index];
37100         this.textNode = cs[index].firstChild;
37101     },
37102
37103     getAnchor : function(){
37104         return this.anchor;
37105     },
37106
37107     getTextEl : function(){
37108         return this.textNode;
37109     },
37110
37111     getIconEl : function(){
37112         return this.iconNode;
37113     },
37114
37115     isChecked : function(){
37116         return this.checkbox ? this.checkbox.checked : false;
37117     },
37118
37119     updateExpandIcon : function(){
37120         if(this.rendered){
37121             var n = this.node, c1, c2;
37122             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37123             var hasChild = n.hasChildNodes();
37124             if(hasChild){
37125                 if(n.expanded){
37126                     cls += "-minus";
37127                     c1 = "x-tree-node-collapsed";
37128                     c2 = "x-tree-node-expanded";
37129                 }else{
37130                     cls += "-plus";
37131                     c1 = "x-tree-node-expanded";
37132                     c2 = "x-tree-node-collapsed";
37133                 }
37134                 if(this.wasLeaf){
37135                     this.removeClass("x-tree-node-leaf");
37136                     this.wasLeaf = false;
37137                 }
37138                 if(this.c1 != c1 || this.c2 != c2){
37139                     Roo.fly(this.elNode).replaceClass(c1, c2);
37140                     this.c1 = c1; this.c2 = c2;
37141                 }
37142             }else{
37143                 // this changes non-leafs into leafs if they have no children.
37144                 // it's not very rational behaviour..
37145                 
37146                 if(!this.wasLeaf && this.node.leaf){
37147                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37148                     delete this.c1;
37149                     delete this.c2;
37150                     this.wasLeaf = true;
37151                 }
37152             }
37153             var ecc = "x-tree-ec-icon "+cls;
37154             if(this.ecc != ecc){
37155                 this.ecNode.className = ecc;
37156                 this.ecc = ecc;
37157             }
37158         }
37159     },
37160
37161     getChildIndent : function(){
37162         if(!this.childIndent){
37163             var buf = [];
37164             var p = this.node;
37165             while(p){
37166                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37167                     if(!p.isLast()) {
37168                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37169                     } else {
37170                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37171                     }
37172                 }
37173                 p = p.parentNode;
37174             }
37175             this.childIndent = buf.join("");
37176         }
37177         return this.childIndent;
37178     },
37179
37180     renderIndent : function(){
37181         if(this.rendered){
37182             var indent = "";
37183             var p = this.node.parentNode;
37184             if(p){
37185                 indent = p.ui.getChildIndent();
37186             }
37187             if(this.indentMarkup != indent){ // don't rerender if not required
37188                 this.indentNode.innerHTML = indent;
37189                 this.indentMarkup = indent;
37190             }
37191             this.updateExpandIcon();
37192         }
37193     }
37194 };
37195
37196 Roo.tree.RootTreeNodeUI = function(){
37197     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37198 };
37199 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37200     render : function(){
37201         if(!this.rendered){
37202             var targetNode = this.node.ownerTree.innerCt.dom;
37203             this.node.expanded = true;
37204             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37205             this.wrap = this.ctNode = targetNode.firstChild;
37206         }
37207     },
37208     collapse : function(){
37209     },
37210     expand : function(){
37211     }
37212 });/*
37213  * Based on:
37214  * Ext JS Library 1.1.1
37215  * Copyright(c) 2006-2007, Ext JS, LLC.
37216  *
37217  * Originally Released Under LGPL - original licence link has changed is not relivant.
37218  *
37219  * Fork - LGPL
37220  * <script type="text/javascript">
37221  */
37222 /**
37223  * @class Roo.tree.TreeLoader
37224  * @extends Roo.util.Observable
37225  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37226  * nodes from a specified URL. The response must be a javascript Array definition
37227  * who's elements are node definition objects. eg:
37228  * <pre><code>
37229 {  success : true,
37230    data :      [
37231    
37232     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37233     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37234     ]
37235 }
37236
37237
37238 </code></pre>
37239  * <br><br>
37240  * The old style respose with just an array is still supported, but not recommended.
37241  * <br><br>
37242  *
37243  * A server request is sent, and child nodes are loaded only when a node is expanded.
37244  * The loading node's id is passed to the server under the parameter name "node" to
37245  * enable the server to produce the correct child nodes.
37246  * <br><br>
37247  * To pass extra parameters, an event handler may be attached to the "beforeload"
37248  * event, and the parameters specified in the TreeLoader's baseParams property:
37249  * <pre><code>
37250     myTreeLoader.on("beforeload", function(treeLoader, node) {
37251         this.baseParams.category = node.attributes.category;
37252     }, this);
37253     
37254 </code></pre>
37255  *
37256  * This would pass an HTTP parameter called "category" to the server containing
37257  * the value of the Node's "category" attribute.
37258  * @constructor
37259  * Creates a new Treeloader.
37260  * @param {Object} config A config object containing config properties.
37261  */
37262 Roo.tree.TreeLoader = function(config){
37263     this.baseParams = {};
37264     this.requestMethod = "POST";
37265     Roo.apply(this, config);
37266
37267     this.addEvents({
37268     
37269         /**
37270          * @event beforeload
37271          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37272          * @param {Object} This TreeLoader object.
37273          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37274          * @param {Object} callback The callback function specified in the {@link #load} call.
37275          */
37276         beforeload : true,
37277         /**
37278          * @event load
37279          * Fires when the node has been successfuly loaded.
37280          * @param {Object} This TreeLoader object.
37281          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37282          * @param {Object} response The response object containing the data from the server.
37283          */
37284         load : true,
37285         /**
37286          * @event loadexception
37287          * Fires if the network request failed.
37288          * @param {Object} This TreeLoader object.
37289          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37290          * @param {Object} response The response object containing the data from the server.
37291          */
37292         loadexception : true,
37293         /**
37294          * @event create
37295          * Fires before a node is created, enabling you to return custom Node types 
37296          * @param {Object} This TreeLoader object.
37297          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37298          */
37299         create : true
37300     });
37301
37302     Roo.tree.TreeLoader.superclass.constructor.call(this);
37303 };
37304
37305 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37306     /**
37307     * @cfg {String} dataUrl The URL from which to request a Json string which
37308     * specifies an array of node definition object representing the child nodes
37309     * to be loaded.
37310     */
37311     /**
37312     * @cfg {String} requestMethod either GET or POST
37313     * defaults to POST (due to BC)
37314     * to be loaded.
37315     */
37316     /**
37317     * @cfg {Object} baseParams (optional) An object containing properties which
37318     * specify HTTP parameters to be passed to each request for child nodes.
37319     */
37320     /**
37321     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37322     * created by this loader. If the attributes sent by the server have an attribute in this object,
37323     * they take priority.
37324     */
37325     /**
37326     * @cfg {Object} uiProviders (optional) An object containing properties which
37327     * 
37328     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37329     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37330     * <i>uiProvider</i> attribute of a returned child node is a string rather
37331     * than a reference to a TreeNodeUI implementation, this that string value
37332     * is used as a property name in the uiProviders object. You can define the provider named
37333     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37334     */
37335     uiProviders : {},
37336
37337     /**
37338     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37339     * child nodes before loading.
37340     */
37341     clearOnLoad : true,
37342
37343     /**
37344     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37345     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37346     * Grid query { data : [ .....] }
37347     */
37348     
37349     root : false,
37350      /**
37351     * @cfg {String} queryParam (optional) 
37352     * Name of the query as it will be passed on the querystring (defaults to 'node')
37353     * eg. the request will be ?node=[id]
37354     */
37355     
37356     
37357     queryParam: false,
37358     
37359     /**
37360      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37361      * This is called automatically when a node is expanded, but may be used to reload
37362      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37363      * @param {Roo.tree.TreeNode} node
37364      * @param {Function} callback
37365      */
37366     load : function(node, callback){
37367         if(this.clearOnLoad){
37368             while(node.firstChild){
37369                 node.removeChild(node.firstChild);
37370             }
37371         }
37372         if(node.attributes.children){ // preloaded json children
37373             var cs = node.attributes.children;
37374             for(var i = 0, len = cs.length; i < len; i++){
37375                 node.appendChild(this.createNode(cs[i]));
37376             }
37377             if(typeof callback == "function"){
37378                 callback();
37379             }
37380         }else if(this.dataUrl){
37381             this.requestData(node, callback);
37382         }
37383     },
37384
37385     getParams: function(node){
37386         var buf = [], bp = this.baseParams;
37387         for(var key in bp){
37388             if(typeof bp[key] != "function"){
37389                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37390             }
37391         }
37392         var n = this.queryParam === false ? 'node' : this.queryParam;
37393         buf.push(n + "=", encodeURIComponent(node.id));
37394         return buf.join("");
37395     },
37396
37397     requestData : function(node, callback){
37398         if(this.fireEvent("beforeload", this, node, callback) !== false){
37399             this.transId = Roo.Ajax.request({
37400                 method:this.requestMethod,
37401                 url: this.dataUrl||this.url,
37402                 success: this.handleResponse,
37403                 failure: this.handleFailure,
37404                 scope: this,
37405                 argument: {callback: callback, node: node},
37406                 params: this.getParams(node)
37407             });
37408         }else{
37409             // if the load is cancelled, make sure we notify
37410             // the node that we are done
37411             if(typeof callback == "function"){
37412                 callback();
37413             }
37414         }
37415     },
37416
37417     isLoading : function(){
37418         return this.transId ? true : false;
37419     },
37420
37421     abort : function(){
37422         if(this.isLoading()){
37423             Roo.Ajax.abort(this.transId);
37424         }
37425     },
37426
37427     // private
37428     createNode : function(attr)
37429     {
37430         // apply baseAttrs, nice idea Corey!
37431         if(this.baseAttrs){
37432             Roo.applyIf(attr, this.baseAttrs);
37433         }
37434         if(this.applyLoader !== false){
37435             attr.loader = this;
37436         }
37437         // uiProvider = depreciated..
37438         
37439         if(typeof(attr.uiProvider) == 'string'){
37440            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37441                 /**  eval:var:attr */ eval(attr.uiProvider);
37442         }
37443         if(typeof(this.uiProviders['default']) != 'undefined') {
37444             attr.uiProvider = this.uiProviders['default'];
37445         }
37446         
37447         this.fireEvent('create', this, attr);
37448         
37449         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37450         return(attr.leaf ?
37451                         new Roo.tree.TreeNode(attr) :
37452                         new Roo.tree.AsyncTreeNode(attr));
37453     },
37454
37455     processResponse : function(response, node, callback)
37456     {
37457         var json = response.responseText;
37458         try {
37459             
37460             var o = Roo.decode(json);
37461             
37462             if (this.root === false && typeof(o.success) != undefined) {
37463                 this.root = 'data'; // the default behaviour for list like data..
37464                 }
37465                 
37466             if (this.root !== false &&  !o.success) {
37467                 // it's a failure condition.
37468                 var a = response.argument;
37469                 this.fireEvent("loadexception", this, a.node, response);
37470                 Roo.log("Load failed - should have a handler really");
37471                 return;
37472             }
37473             
37474             
37475             
37476             if (this.root !== false) {
37477                  o = o[this.root];
37478             }
37479             
37480             for(var i = 0, len = o.length; i < len; i++){
37481                 var n = this.createNode(o[i]);
37482                 if(n){
37483                     node.appendChild(n);
37484                 }
37485             }
37486             if(typeof callback == "function"){
37487                 callback(this, node);
37488             }
37489         }catch(e){
37490             this.handleFailure(response);
37491         }
37492     },
37493
37494     handleResponse : function(response){
37495         this.transId = false;
37496         var a = response.argument;
37497         this.processResponse(response, a.node, a.callback);
37498         this.fireEvent("load", this, a.node, response);
37499     },
37500
37501     handleFailure : function(response)
37502     {
37503         // should handle failure better..
37504         this.transId = false;
37505         var a = response.argument;
37506         this.fireEvent("loadexception", this, a.node, response);
37507         if(typeof a.callback == "function"){
37508             a.callback(this, a.node);
37509         }
37510     }
37511 });/*
37512  * Based on:
37513  * Ext JS Library 1.1.1
37514  * Copyright(c) 2006-2007, Ext JS, LLC.
37515  *
37516  * Originally Released Under LGPL - original licence link has changed is not relivant.
37517  *
37518  * Fork - LGPL
37519  * <script type="text/javascript">
37520  */
37521
37522 /**
37523 * @class Roo.tree.TreeFilter
37524 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37525 * @param {TreePanel} tree
37526 * @param {Object} config (optional)
37527  */
37528 Roo.tree.TreeFilter = function(tree, config){
37529     this.tree = tree;
37530     this.filtered = {};
37531     Roo.apply(this, config);
37532 };
37533
37534 Roo.tree.TreeFilter.prototype = {
37535     clearBlank:false,
37536     reverse:false,
37537     autoClear:false,
37538     remove:false,
37539
37540      /**
37541      * Filter the data by a specific attribute.
37542      * @param {String/RegExp} value Either string that the attribute value
37543      * should start with or a RegExp to test against the attribute
37544      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37545      * @param {TreeNode} startNode (optional) The node to start the filter at.
37546      */
37547     filter : function(value, attr, startNode){
37548         attr = attr || "text";
37549         var f;
37550         if(typeof value == "string"){
37551             var vlen = value.length;
37552             // auto clear empty filter
37553             if(vlen == 0 && this.clearBlank){
37554                 this.clear();
37555                 return;
37556             }
37557             value = value.toLowerCase();
37558             f = function(n){
37559                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37560             };
37561         }else if(value.exec){ // regex?
37562             f = function(n){
37563                 return value.test(n.attributes[attr]);
37564             };
37565         }else{
37566             throw 'Illegal filter type, must be string or regex';
37567         }
37568         this.filterBy(f, null, startNode);
37569         },
37570
37571     /**
37572      * Filter by a function. The passed function will be called with each
37573      * node in the tree (or from the startNode). If the function returns true, the node is kept
37574      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37575      * @param {Function} fn The filter function
37576      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37577      */
37578     filterBy : function(fn, scope, startNode){
37579         startNode = startNode || this.tree.root;
37580         if(this.autoClear){
37581             this.clear();
37582         }
37583         var af = this.filtered, rv = this.reverse;
37584         var f = function(n){
37585             if(n == startNode){
37586                 return true;
37587             }
37588             if(af[n.id]){
37589                 return false;
37590             }
37591             var m = fn.call(scope || n, n);
37592             if(!m || rv){
37593                 af[n.id] = n;
37594                 n.ui.hide();
37595                 return false;
37596             }
37597             return true;
37598         };
37599         startNode.cascade(f);
37600         if(this.remove){
37601            for(var id in af){
37602                if(typeof id != "function"){
37603                    var n = af[id];
37604                    if(n && n.parentNode){
37605                        n.parentNode.removeChild(n);
37606                    }
37607                }
37608            }
37609         }
37610     },
37611
37612     /**
37613      * Clears the current filter. Note: with the "remove" option
37614      * set a filter cannot be cleared.
37615      */
37616     clear : function(){
37617         var t = this.tree;
37618         var af = this.filtered;
37619         for(var id in af){
37620             if(typeof id != "function"){
37621                 var n = af[id];
37622                 if(n){
37623                     n.ui.show();
37624                 }
37625             }
37626         }
37627         this.filtered = {};
37628     }
37629 };
37630 /*
37631  * Based on:
37632  * Ext JS Library 1.1.1
37633  * Copyright(c) 2006-2007, Ext JS, LLC.
37634  *
37635  * Originally Released Under LGPL - original licence link has changed is not relivant.
37636  *
37637  * Fork - LGPL
37638  * <script type="text/javascript">
37639  */
37640  
37641
37642 /**
37643  * @class Roo.tree.TreeSorter
37644  * Provides sorting of nodes in a TreePanel
37645  * 
37646  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37647  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37648  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37649  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37650  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37651  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37652  * @constructor
37653  * @param {TreePanel} tree
37654  * @param {Object} config
37655  */
37656 Roo.tree.TreeSorter = function(tree, config){
37657     Roo.apply(this, config);
37658     tree.on("beforechildrenrendered", this.doSort, this);
37659     tree.on("append", this.updateSort, this);
37660     tree.on("insert", this.updateSort, this);
37661     
37662     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37663     var p = this.property || "text";
37664     var sortType = this.sortType;
37665     var fs = this.folderSort;
37666     var cs = this.caseSensitive === true;
37667     var leafAttr = this.leafAttr || 'leaf';
37668
37669     this.sortFn = function(n1, n2){
37670         if(fs){
37671             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37672                 return 1;
37673             }
37674             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37675                 return -1;
37676             }
37677         }
37678         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37679         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37680         if(v1 < v2){
37681                         return dsc ? +1 : -1;
37682                 }else if(v1 > v2){
37683                         return dsc ? -1 : +1;
37684         }else{
37685                 return 0;
37686         }
37687     };
37688 };
37689
37690 Roo.tree.TreeSorter.prototype = {
37691     doSort : function(node){
37692         node.sort(this.sortFn);
37693     },
37694     
37695     compareNodes : function(n1, n2){
37696         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37697     },
37698     
37699     updateSort : function(tree, node){
37700         if(node.childrenRendered){
37701             this.doSort.defer(1, this, [node]);
37702         }
37703     }
37704 };/*
37705  * Based on:
37706  * Ext JS Library 1.1.1
37707  * Copyright(c) 2006-2007, Ext JS, LLC.
37708  *
37709  * Originally Released Under LGPL - original licence link has changed is not relivant.
37710  *
37711  * Fork - LGPL
37712  * <script type="text/javascript">
37713  */
37714
37715 if(Roo.dd.DropZone){
37716     
37717 Roo.tree.TreeDropZone = function(tree, config){
37718     this.allowParentInsert = false;
37719     this.allowContainerDrop = false;
37720     this.appendOnly = false;
37721     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37722     this.tree = tree;
37723     this.lastInsertClass = "x-tree-no-status";
37724     this.dragOverData = {};
37725 };
37726
37727 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37728     ddGroup : "TreeDD",
37729     scroll:  true,
37730     
37731     expandDelay : 1000,
37732     
37733     expandNode : function(node){
37734         if(node.hasChildNodes() && !node.isExpanded()){
37735             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37736         }
37737     },
37738     
37739     queueExpand : function(node){
37740         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37741     },
37742     
37743     cancelExpand : function(){
37744         if(this.expandProcId){
37745             clearTimeout(this.expandProcId);
37746             this.expandProcId = false;
37747         }
37748     },
37749     
37750     isValidDropPoint : function(n, pt, dd, e, data){
37751         if(!n || !data){ return false; }
37752         var targetNode = n.node;
37753         var dropNode = data.node;
37754         // default drop rules
37755         if(!(targetNode && targetNode.isTarget && pt)){
37756             return false;
37757         }
37758         if(pt == "append" && targetNode.allowChildren === false){
37759             return false;
37760         }
37761         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37762             return false;
37763         }
37764         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37765             return false;
37766         }
37767         // reuse the object
37768         var overEvent = this.dragOverData;
37769         overEvent.tree = this.tree;
37770         overEvent.target = targetNode;
37771         overEvent.data = data;
37772         overEvent.point = pt;
37773         overEvent.source = dd;
37774         overEvent.rawEvent = e;
37775         overEvent.dropNode = dropNode;
37776         overEvent.cancel = false;  
37777         var result = this.tree.fireEvent("nodedragover", overEvent);
37778         return overEvent.cancel === false && result !== false;
37779     },
37780     
37781     getDropPoint : function(e, n, dd)
37782     {
37783         var tn = n.node;
37784         if(tn.isRoot){
37785             return tn.allowChildren !== false ? "append" : false; // always append for root
37786         }
37787         var dragEl = n.ddel;
37788         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37789         var y = Roo.lib.Event.getPageY(e);
37790         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37791         
37792         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37793         var noAppend = tn.allowChildren === false;
37794         if(this.appendOnly || tn.parentNode.allowChildren === false){
37795             return noAppend ? false : "append";
37796         }
37797         var noBelow = false;
37798         if(!this.allowParentInsert){
37799             noBelow = tn.hasChildNodes() && tn.isExpanded();
37800         }
37801         var q = (b - t) / (noAppend ? 2 : 3);
37802         if(y >= t && y < (t + q)){
37803             return "above";
37804         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37805             return "below";
37806         }else{
37807             return "append";
37808         }
37809     },
37810     
37811     onNodeEnter : function(n, dd, e, data)
37812     {
37813         this.cancelExpand();
37814     },
37815     
37816     onNodeOver : function(n, dd, e, data)
37817     {
37818        
37819         var pt = this.getDropPoint(e, n, dd);
37820         var node = n.node;
37821         
37822         // auto node expand check
37823         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37824             this.queueExpand(node);
37825         }else if(pt != "append"){
37826             this.cancelExpand();
37827         }
37828         
37829         // set the insert point style on the target node
37830         var returnCls = this.dropNotAllowed;
37831         if(this.isValidDropPoint(n, pt, dd, e, data)){
37832            if(pt){
37833                var el = n.ddel;
37834                var cls;
37835                if(pt == "above"){
37836                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37837                    cls = "x-tree-drag-insert-above";
37838                }else if(pt == "below"){
37839                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37840                    cls = "x-tree-drag-insert-below";
37841                }else{
37842                    returnCls = "x-tree-drop-ok-append";
37843                    cls = "x-tree-drag-append";
37844                }
37845                if(this.lastInsertClass != cls){
37846                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37847                    this.lastInsertClass = cls;
37848                }
37849            }
37850        }
37851        return returnCls;
37852     },
37853     
37854     onNodeOut : function(n, dd, e, data){
37855         
37856         this.cancelExpand();
37857         this.removeDropIndicators(n);
37858     },
37859     
37860     onNodeDrop : function(n, dd, e, data){
37861         var point = this.getDropPoint(e, n, dd);
37862         var targetNode = n.node;
37863         targetNode.ui.startDrop();
37864         if(!this.isValidDropPoint(n, point, dd, e, data)){
37865             targetNode.ui.endDrop();
37866             return false;
37867         }
37868         // first try to find the drop node
37869         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37870         var dropEvent = {
37871             tree : this.tree,
37872             target: targetNode,
37873             data: data,
37874             point: point,
37875             source: dd,
37876             rawEvent: e,
37877             dropNode: dropNode,
37878             cancel: !dropNode   
37879         };
37880         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37881         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37882             targetNode.ui.endDrop();
37883             return false;
37884         }
37885         // allow target changing
37886         targetNode = dropEvent.target;
37887         if(point == "append" && !targetNode.isExpanded()){
37888             targetNode.expand(false, null, function(){
37889                 this.completeDrop(dropEvent);
37890             }.createDelegate(this));
37891         }else{
37892             this.completeDrop(dropEvent);
37893         }
37894         return true;
37895     },
37896     
37897     completeDrop : function(de){
37898         var ns = de.dropNode, p = de.point, t = de.target;
37899         if(!(ns instanceof Array)){
37900             ns = [ns];
37901         }
37902         var n;
37903         for(var i = 0, len = ns.length; i < len; i++){
37904             n = ns[i];
37905             if(p == "above"){
37906                 t.parentNode.insertBefore(n, t);
37907             }else if(p == "below"){
37908                 t.parentNode.insertBefore(n, t.nextSibling);
37909             }else{
37910                 t.appendChild(n);
37911             }
37912         }
37913         n.ui.focus();
37914         if(this.tree.hlDrop){
37915             n.ui.highlight();
37916         }
37917         t.ui.endDrop();
37918         this.tree.fireEvent("nodedrop", de);
37919     },
37920     
37921     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37922         if(this.tree.hlDrop){
37923             dropNode.ui.focus();
37924             dropNode.ui.highlight();
37925         }
37926         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37927     },
37928     
37929     getTree : function(){
37930         return this.tree;
37931     },
37932     
37933     removeDropIndicators : function(n){
37934         if(n && n.ddel){
37935             var el = n.ddel;
37936             Roo.fly(el).removeClass([
37937                     "x-tree-drag-insert-above",
37938                     "x-tree-drag-insert-below",
37939                     "x-tree-drag-append"]);
37940             this.lastInsertClass = "_noclass";
37941         }
37942     },
37943     
37944     beforeDragDrop : function(target, e, id){
37945         this.cancelExpand();
37946         return true;
37947     },
37948     
37949     afterRepair : function(data){
37950         if(data && Roo.enableFx){
37951             data.node.ui.highlight();
37952         }
37953         this.hideProxy();
37954     } 
37955     
37956 });
37957
37958 }
37959 /*
37960  * Based on:
37961  * Ext JS Library 1.1.1
37962  * Copyright(c) 2006-2007, Ext JS, LLC.
37963  *
37964  * Originally Released Under LGPL - original licence link has changed is not relivant.
37965  *
37966  * Fork - LGPL
37967  * <script type="text/javascript">
37968  */
37969  
37970
37971 if(Roo.dd.DragZone){
37972 Roo.tree.TreeDragZone = function(tree, config){
37973     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37974     this.tree = tree;
37975 };
37976
37977 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37978     ddGroup : "TreeDD",
37979    
37980     onBeforeDrag : function(data, e){
37981         var n = data.node;
37982         return n && n.draggable && !n.disabled;
37983     },
37984      
37985     
37986     onInitDrag : function(e){
37987         var data = this.dragData;
37988         this.tree.getSelectionModel().select(data.node);
37989         this.proxy.update("");
37990         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37991         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37992     },
37993     
37994     getRepairXY : function(e, data){
37995         return data.node.ui.getDDRepairXY();
37996     },
37997     
37998     onEndDrag : function(data, e){
37999         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38000         
38001         
38002     },
38003     
38004     onValidDrop : function(dd, e, id){
38005         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38006         this.hideProxy();
38007     },
38008     
38009     beforeInvalidDrop : function(e, id){
38010         // this scrolls the original position back into view
38011         var sm = this.tree.getSelectionModel();
38012         sm.clearSelections();
38013         sm.select(this.dragData.node);
38014     }
38015 });
38016 }/*
38017  * Based on:
38018  * Ext JS Library 1.1.1
38019  * Copyright(c) 2006-2007, Ext JS, LLC.
38020  *
38021  * Originally Released Under LGPL - original licence link has changed is not relivant.
38022  *
38023  * Fork - LGPL
38024  * <script type="text/javascript">
38025  */
38026 /**
38027  * @class Roo.tree.TreeEditor
38028  * @extends Roo.Editor
38029  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38030  * as the editor field.
38031  * @constructor
38032  * @param {Object} config (used to be the tree panel.)
38033  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38034  * 
38035  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38036  * @cfg {Roo.form.TextField} field [required] The field configuration
38037  *
38038  * 
38039  */
38040 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38041     var tree = config;
38042     var field;
38043     if (oldconfig) { // old style..
38044         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38045     } else {
38046         // new style..
38047         tree = config.tree;
38048         config.field = config.field  || {};
38049         config.field.xtype = 'TextField';
38050         field = Roo.factory(config.field, Roo.form);
38051     }
38052     config = config || {};
38053     
38054     
38055     this.addEvents({
38056         /**
38057          * @event beforenodeedit
38058          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38059          * false from the handler of this event.
38060          * @param {Editor} this
38061          * @param {Roo.tree.Node} node 
38062          */
38063         "beforenodeedit" : true
38064     });
38065     
38066     //Roo.log(config);
38067     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38068
38069     this.tree = tree;
38070
38071     tree.on('beforeclick', this.beforeNodeClick, this);
38072     tree.getTreeEl().on('mousedown', this.hide, this);
38073     this.on('complete', this.updateNode, this);
38074     this.on('beforestartedit', this.fitToTree, this);
38075     this.on('startedit', this.bindScroll, this, {delay:10});
38076     this.on('specialkey', this.onSpecialKey, this);
38077 };
38078
38079 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38080     /**
38081      * @cfg {String} alignment
38082      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38083      */
38084     alignment: "l-l",
38085     // inherit
38086     autoSize: false,
38087     /**
38088      * @cfg {Boolean} hideEl
38089      * True to hide the bound element while the editor is displayed (defaults to false)
38090      */
38091     hideEl : false,
38092     /**
38093      * @cfg {String} cls
38094      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38095      */
38096     cls: "x-small-editor x-tree-editor",
38097     /**
38098      * @cfg {Boolean} shim
38099      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38100      */
38101     shim:false,
38102     // inherit
38103     shadow:"frame",
38104     /**
38105      * @cfg {Number} maxWidth
38106      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38107      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38108      * scroll and client offsets into account prior to each edit.
38109      */
38110     maxWidth: 250,
38111
38112     editDelay : 350,
38113
38114     // private
38115     fitToTree : function(ed, el){
38116         var td = this.tree.getTreeEl().dom, nd = el.dom;
38117         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38118             td.scrollLeft = nd.offsetLeft;
38119         }
38120         var w = Math.min(
38121                 this.maxWidth,
38122                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38123         this.setSize(w, '');
38124         
38125         return this.fireEvent('beforenodeedit', this, this.editNode);
38126         
38127     },
38128
38129     // private
38130     triggerEdit : function(node){
38131         this.completeEdit();
38132         this.editNode = node;
38133         this.startEdit(node.ui.textNode, node.text);
38134     },
38135
38136     // private
38137     bindScroll : function(){
38138         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38139     },
38140
38141     // private
38142     beforeNodeClick : function(node, e){
38143         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38144         this.lastClick = new Date();
38145         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38146             e.stopEvent();
38147             this.triggerEdit(node);
38148             return false;
38149         }
38150         return true;
38151     },
38152
38153     // private
38154     updateNode : function(ed, value){
38155         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38156         this.editNode.setText(value);
38157     },
38158
38159     // private
38160     onHide : function(){
38161         Roo.tree.TreeEditor.superclass.onHide.call(this);
38162         if(this.editNode){
38163             this.editNode.ui.focus();
38164         }
38165     },
38166
38167     // private
38168     onSpecialKey : function(field, e){
38169         var k = e.getKey();
38170         if(k == e.ESC){
38171             e.stopEvent();
38172             this.cancelEdit();
38173         }else if(k == e.ENTER && !e.hasModifier()){
38174             e.stopEvent();
38175             this.completeEdit();
38176         }
38177     }
38178 });//<Script type="text/javascript">
38179 /*
38180  * Based on:
38181  * Ext JS Library 1.1.1
38182  * Copyright(c) 2006-2007, Ext JS, LLC.
38183  *
38184  * Originally Released Under LGPL - original licence link has changed is not relivant.
38185  *
38186  * Fork - LGPL
38187  * <script type="text/javascript">
38188  */
38189  
38190 /**
38191  * Not documented??? - probably should be...
38192  */
38193
38194 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38195     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38196     
38197     renderElements : function(n, a, targetNode, bulkRender){
38198         //consel.log("renderElements?");
38199         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38200
38201         var t = n.getOwnerTree();
38202         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38203         
38204         var cols = t.columns;
38205         var bw = t.borderWidth;
38206         var c = cols[0];
38207         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38208          var cb = typeof a.checked == "boolean";
38209         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38210         var colcls = 'x-t-' + tid + '-c0';
38211         var buf = [
38212             '<li class="x-tree-node">',
38213             
38214                 
38215                 '<div class="x-tree-node-el ', a.cls,'">',
38216                     // extran...
38217                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38218                 
38219                 
38220                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38221                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38222                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38223                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38224                            (a.iconCls ? ' '+a.iconCls : ''),
38225                            '" unselectable="on" />',
38226                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38227                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38228                              
38229                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38230                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38231                             '<span unselectable="on" qtip="' + tx + '">',
38232                              tx,
38233                              '</span></a>' ,
38234                     '</div>',
38235                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38236                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38237                  ];
38238         for(var i = 1, len = cols.length; i < len; i++){
38239             c = cols[i];
38240             colcls = 'x-t-' + tid + '-c' +i;
38241             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38242             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38243                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38244                       "</div>");
38245          }
38246          
38247          buf.push(
38248             '</a>',
38249             '<div class="x-clear"></div></div>',
38250             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38251             "</li>");
38252         
38253         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38254             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38255                                 n.nextSibling.ui.getEl(), buf.join(""));
38256         }else{
38257             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38258         }
38259         var el = this.wrap.firstChild;
38260         this.elRow = el;
38261         this.elNode = el.firstChild;
38262         this.ranchor = el.childNodes[1];
38263         this.ctNode = this.wrap.childNodes[1];
38264         var cs = el.firstChild.childNodes;
38265         this.indentNode = cs[0];
38266         this.ecNode = cs[1];
38267         this.iconNode = cs[2];
38268         var index = 3;
38269         if(cb){
38270             this.checkbox = cs[3];
38271             index++;
38272         }
38273         this.anchor = cs[index];
38274         
38275         this.textNode = cs[index].firstChild;
38276         
38277         //el.on("click", this.onClick, this);
38278         //el.on("dblclick", this.onDblClick, this);
38279         
38280         
38281        // console.log(this);
38282     },
38283     initEvents : function(){
38284         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38285         
38286             
38287         var a = this.ranchor;
38288
38289         var el = Roo.get(a);
38290
38291         if(Roo.isOpera){ // opera render bug ignores the CSS
38292             el.setStyle("text-decoration", "none");
38293         }
38294
38295         el.on("click", this.onClick, this);
38296         el.on("dblclick", this.onDblClick, this);
38297         el.on("contextmenu", this.onContextMenu, this);
38298         
38299     },
38300     
38301     /*onSelectedChange : function(state){
38302         if(state){
38303             this.focus();
38304             this.addClass("x-tree-selected");
38305         }else{
38306             //this.blur();
38307             this.removeClass("x-tree-selected");
38308         }
38309     },*/
38310     addClass : function(cls){
38311         if(this.elRow){
38312             Roo.fly(this.elRow).addClass(cls);
38313         }
38314         
38315     },
38316     
38317     
38318     removeClass : function(cls){
38319         if(this.elRow){
38320             Roo.fly(this.elRow).removeClass(cls);
38321         }
38322     }
38323
38324     
38325     
38326 });//<Script type="text/javascript">
38327
38328 /*
38329  * Based on:
38330  * Ext JS Library 1.1.1
38331  * Copyright(c) 2006-2007, Ext JS, LLC.
38332  *
38333  * Originally Released Under LGPL - original licence link has changed is not relivant.
38334  *
38335  * Fork - LGPL
38336  * <script type="text/javascript">
38337  */
38338  
38339
38340 /**
38341  * @class Roo.tree.ColumnTree
38342  * @extends Roo.tree.TreePanel
38343  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38344  * @cfg {int} borderWidth  compined right/left border allowance
38345  * @constructor
38346  * @param {String/HTMLElement/Element} el The container element
38347  * @param {Object} config
38348  */
38349 Roo.tree.ColumnTree =  function(el, config)
38350 {
38351    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38352    this.addEvents({
38353         /**
38354         * @event resize
38355         * Fire this event on a container when it resizes
38356         * @param {int} w Width
38357         * @param {int} h Height
38358         */
38359        "resize" : true
38360     });
38361     this.on('resize', this.onResize, this);
38362 };
38363
38364 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38365     //lines:false,
38366     
38367     
38368     borderWidth: Roo.isBorderBox ? 0 : 2, 
38369     headEls : false,
38370     
38371     render : function(){
38372         // add the header.....
38373        
38374         Roo.tree.ColumnTree.superclass.render.apply(this);
38375         
38376         this.el.addClass('x-column-tree');
38377         
38378         this.headers = this.el.createChild(
38379             {cls:'x-tree-headers'},this.innerCt.dom);
38380    
38381         var cols = this.columns, c;
38382         var totalWidth = 0;
38383         this.headEls = [];
38384         var  len = cols.length;
38385         for(var i = 0; i < len; i++){
38386              c = cols[i];
38387              totalWidth += c.width;
38388             this.headEls.push(this.headers.createChild({
38389                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38390                  cn: {
38391                      cls:'x-tree-hd-text',
38392                      html: c.header
38393                  },
38394                  style:'width:'+(c.width-this.borderWidth)+'px;'
38395              }));
38396         }
38397         this.headers.createChild({cls:'x-clear'});
38398         // prevent floats from wrapping when clipped
38399         this.headers.setWidth(totalWidth);
38400         //this.innerCt.setWidth(totalWidth);
38401         this.innerCt.setStyle({ overflow: 'auto' });
38402         this.onResize(this.width, this.height);
38403              
38404         
38405     },
38406     onResize : function(w,h)
38407     {
38408         this.height = h;
38409         this.width = w;
38410         // resize cols..
38411         this.innerCt.setWidth(this.width);
38412         this.innerCt.setHeight(this.height-20);
38413         
38414         // headers...
38415         var cols = this.columns, c;
38416         var totalWidth = 0;
38417         var expEl = false;
38418         var len = cols.length;
38419         for(var i = 0; i < len; i++){
38420             c = cols[i];
38421             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38422                 // it's the expander..
38423                 expEl  = this.headEls[i];
38424                 continue;
38425             }
38426             totalWidth += c.width;
38427             
38428         }
38429         if (expEl) {
38430             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38431         }
38432         this.headers.setWidth(w-20);
38433
38434         
38435         
38436         
38437     }
38438 });
38439 /*
38440  * Based on:
38441  * Ext JS Library 1.1.1
38442  * Copyright(c) 2006-2007, Ext JS, LLC.
38443  *
38444  * Originally Released Under LGPL - original licence link has changed is not relivant.
38445  *
38446  * Fork - LGPL
38447  * <script type="text/javascript">
38448  */
38449  
38450 /**
38451  * @class Roo.menu.Menu
38452  * @extends Roo.util.Observable
38453  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38454  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38455  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38456  * @constructor
38457  * Creates a new Menu
38458  * @param {Object} config Configuration options
38459  */
38460 Roo.menu.Menu = function(config){
38461     
38462     Roo.menu.Menu.superclass.constructor.call(this, config);
38463     
38464     this.id = this.id || Roo.id();
38465     this.addEvents({
38466         /**
38467          * @event beforeshow
38468          * Fires before this menu is displayed
38469          * @param {Roo.menu.Menu} this
38470          */
38471         beforeshow : true,
38472         /**
38473          * @event beforehide
38474          * Fires before this menu is hidden
38475          * @param {Roo.menu.Menu} this
38476          */
38477         beforehide : true,
38478         /**
38479          * @event show
38480          * Fires after this menu is displayed
38481          * @param {Roo.menu.Menu} this
38482          */
38483         show : true,
38484         /**
38485          * @event hide
38486          * Fires after this menu is hidden
38487          * @param {Roo.menu.Menu} this
38488          */
38489         hide : true,
38490         /**
38491          * @event click
38492          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38493          * @param {Roo.menu.Menu} this
38494          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38495          * @param {Roo.EventObject} e
38496          */
38497         click : true,
38498         /**
38499          * @event mouseover
38500          * Fires when the mouse is hovering over this menu
38501          * @param {Roo.menu.Menu} this
38502          * @param {Roo.EventObject} e
38503          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38504          */
38505         mouseover : true,
38506         /**
38507          * @event mouseout
38508          * Fires when the mouse exits this menu
38509          * @param {Roo.menu.Menu} this
38510          * @param {Roo.EventObject} e
38511          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38512          */
38513         mouseout : true,
38514         /**
38515          * @event itemclick
38516          * Fires when a menu item contained in this menu is clicked
38517          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38518          * @param {Roo.EventObject} e
38519          */
38520         itemclick: true
38521     });
38522     if (this.registerMenu) {
38523         Roo.menu.MenuMgr.register(this);
38524     }
38525     
38526     var mis = this.items;
38527     this.items = new Roo.util.MixedCollection();
38528     if(mis){
38529         this.add.apply(this, mis);
38530     }
38531 };
38532
38533 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38534     /**
38535      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38536      */
38537     minWidth : 120,
38538     /**
38539      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38540      * for bottom-right shadow (defaults to "sides")
38541      */
38542     shadow : "sides",
38543     /**
38544      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38545      * this menu (defaults to "tl-tr?")
38546      */
38547     subMenuAlign : "tl-tr?",
38548     /**
38549      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38550      * relative to its element of origin (defaults to "tl-bl?")
38551      */
38552     defaultAlign : "tl-bl?",
38553     /**
38554      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38555      */
38556     allowOtherMenus : false,
38557     /**
38558      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38559      */
38560     registerMenu : true,
38561
38562     hidden:true,
38563
38564     // private
38565     render : function(){
38566         if(this.el){
38567             return;
38568         }
38569         var el = this.el = new Roo.Layer({
38570             cls: "x-menu",
38571             shadow:this.shadow,
38572             constrain: false,
38573             parentEl: this.parentEl || document.body,
38574             zindex:15000
38575         });
38576
38577         this.keyNav = new Roo.menu.MenuNav(this);
38578
38579         if(this.plain){
38580             el.addClass("x-menu-plain");
38581         }
38582         if(this.cls){
38583             el.addClass(this.cls);
38584         }
38585         // generic focus element
38586         this.focusEl = el.createChild({
38587             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38588         });
38589         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38590         //disabling touch- as it's causing issues ..
38591         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38592         ul.on('click'   , this.onClick, this);
38593         
38594         
38595         ul.on("mouseover", this.onMouseOver, this);
38596         ul.on("mouseout", this.onMouseOut, this);
38597         this.items.each(function(item){
38598             if (item.hidden) {
38599                 return;
38600             }
38601             
38602             var li = document.createElement("li");
38603             li.className = "x-menu-list-item";
38604             ul.dom.appendChild(li);
38605             item.render(li, this);
38606         }, this);
38607         this.ul = ul;
38608         this.autoWidth();
38609     },
38610
38611     // private
38612     autoWidth : function(){
38613         var el = this.el, ul = this.ul;
38614         if(!el){
38615             return;
38616         }
38617         var w = this.width;
38618         if(w){
38619             el.setWidth(w);
38620         }else if(Roo.isIE){
38621             el.setWidth(this.minWidth);
38622             var t = el.dom.offsetWidth; // force recalc
38623             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38624         }
38625     },
38626
38627     // private
38628     delayAutoWidth : function(){
38629         if(this.rendered){
38630             if(!this.awTask){
38631                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38632             }
38633             this.awTask.delay(20);
38634         }
38635     },
38636
38637     // private
38638     findTargetItem : function(e){
38639         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38640         if(t && t.menuItemId){
38641             return this.items.get(t.menuItemId);
38642         }
38643     },
38644
38645     // private
38646     onClick : function(e){
38647         Roo.log("menu.onClick");
38648         var t = this.findTargetItem(e);
38649         if(!t){
38650             return;
38651         }
38652         Roo.log(e);
38653         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38654             if(t == this.activeItem && t.shouldDeactivate(e)){
38655                 this.activeItem.deactivate();
38656                 delete this.activeItem;
38657                 return;
38658             }
38659             if(t.canActivate){
38660                 this.setActiveItem(t, true);
38661             }
38662             return;
38663             
38664             
38665         }
38666         
38667         t.onClick(e);
38668         this.fireEvent("click", this, t, e);
38669     },
38670
38671     // private
38672     setActiveItem : function(item, autoExpand){
38673         if(item != this.activeItem){
38674             if(this.activeItem){
38675                 this.activeItem.deactivate();
38676             }
38677             this.activeItem = item;
38678             item.activate(autoExpand);
38679         }else if(autoExpand){
38680             item.expandMenu();
38681         }
38682     },
38683
38684     // private
38685     tryActivate : function(start, step){
38686         var items = this.items;
38687         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38688             var item = items.get(i);
38689             if(!item.disabled && item.canActivate){
38690                 this.setActiveItem(item, false);
38691                 return item;
38692             }
38693         }
38694         return false;
38695     },
38696
38697     // private
38698     onMouseOver : function(e){
38699         var t;
38700         if(t = this.findTargetItem(e)){
38701             if(t.canActivate && !t.disabled){
38702                 this.setActiveItem(t, true);
38703             }
38704         }
38705         this.fireEvent("mouseover", this, e, t);
38706     },
38707
38708     // private
38709     onMouseOut : function(e){
38710         var t;
38711         if(t = this.findTargetItem(e)){
38712             if(t == this.activeItem && t.shouldDeactivate(e)){
38713                 this.activeItem.deactivate();
38714                 delete this.activeItem;
38715             }
38716         }
38717         this.fireEvent("mouseout", this, e, t);
38718     },
38719
38720     /**
38721      * Read-only.  Returns true if the menu is currently displayed, else false.
38722      * @type Boolean
38723      */
38724     isVisible : function(){
38725         return this.el && !this.hidden;
38726     },
38727
38728     /**
38729      * Displays this menu relative to another element
38730      * @param {String/HTMLElement/Roo.Element} element The element to align to
38731      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38732      * the element (defaults to this.defaultAlign)
38733      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38734      */
38735     show : function(el, pos, parentMenu){
38736         this.parentMenu = parentMenu;
38737         if(!this.el){
38738             this.render();
38739         }
38740         this.fireEvent("beforeshow", this);
38741         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38742     },
38743
38744     /**
38745      * Displays this menu at a specific xy position
38746      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38747      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38748      */
38749     showAt : function(xy, parentMenu, /* private: */_e){
38750         this.parentMenu = parentMenu;
38751         if(!this.el){
38752             this.render();
38753         }
38754         if(_e !== false){
38755             this.fireEvent("beforeshow", this);
38756             xy = this.el.adjustForConstraints(xy);
38757         }
38758         this.el.setXY(xy);
38759         this.el.show();
38760         this.hidden = false;
38761         this.focus();
38762         this.fireEvent("show", this);
38763     },
38764
38765     focus : function(){
38766         if(!this.hidden){
38767             this.doFocus.defer(50, this);
38768         }
38769     },
38770
38771     doFocus : function(){
38772         if(!this.hidden){
38773             this.focusEl.focus();
38774         }
38775     },
38776
38777     /**
38778      * Hides this menu and optionally all parent menus
38779      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38780      */
38781     hide : function(deep){
38782         if(this.el && this.isVisible()){
38783             this.fireEvent("beforehide", this);
38784             if(this.activeItem){
38785                 this.activeItem.deactivate();
38786                 this.activeItem = null;
38787             }
38788             this.el.hide();
38789             this.hidden = true;
38790             this.fireEvent("hide", this);
38791         }
38792         if(deep === true && this.parentMenu){
38793             this.parentMenu.hide(true);
38794         }
38795     },
38796
38797     /**
38798      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38799      * Any of the following are valid:
38800      * <ul>
38801      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38802      * <li>An HTMLElement object which will be converted to a menu item</li>
38803      * <li>A menu item config object that will be created as a new menu item</li>
38804      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38805      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38806      * </ul>
38807      * Usage:
38808      * <pre><code>
38809 // Create the menu
38810 var menu = new Roo.menu.Menu();
38811
38812 // Create a menu item to add by reference
38813 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38814
38815 // Add a bunch of items at once using different methods.
38816 // Only the last item added will be returned.
38817 var item = menu.add(
38818     menuItem,                // add existing item by ref
38819     'Dynamic Item',          // new TextItem
38820     '-',                     // new separator
38821     { text: 'Config Item' }  // new item by config
38822 );
38823 </code></pre>
38824      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38825      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38826      */
38827     add : function(){
38828         var a = arguments, l = a.length, item;
38829         for(var i = 0; i < l; i++){
38830             var el = a[i];
38831             if ((typeof(el) == "object") && el.xtype && el.xns) {
38832                 el = Roo.factory(el, Roo.menu);
38833             }
38834             
38835             if(el.render){ // some kind of Item
38836                 item = this.addItem(el);
38837             }else if(typeof el == "string"){ // string
38838                 if(el == "separator" || el == "-"){
38839                     item = this.addSeparator();
38840                 }else{
38841                     item = this.addText(el);
38842                 }
38843             }else if(el.tagName || el.el){ // element
38844                 item = this.addElement(el);
38845             }else if(typeof el == "object"){ // must be menu item config?
38846                 item = this.addMenuItem(el);
38847             }
38848         }
38849         return item;
38850     },
38851
38852     /**
38853      * Returns this menu's underlying {@link Roo.Element} object
38854      * @return {Roo.Element} The element
38855      */
38856     getEl : function(){
38857         if(!this.el){
38858             this.render();
38859         }
38860         return this.el;
38861     },
38862
38863     /**
38864      * Adds a separator bar to the menu
38865      * @return {Roo.menu.Item} The menu item that was added
38866      */
38867     addSeparator : function(){
38868         return this.addItem(new Roo.menu.Separator());
38869     },
38870
38871     /**
38872      * Adds an {@link Roo.Element} object to the menu
38873      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38874      * @return {Roo.menu.Item} The menu item that was added
38875      */
38876     addElement : function(el){
38877         return this.addItem(new Roo.menu.BaseItem(el));
38878     },
38879
38880     /**
38881      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38882      * @param {Roo.menu.Item} item The menu item to add
38883      * @return {Roo.menu.Item} The menu item that was added
38884      */
38885     addItem : function(item){
38886         this.items.add(item);
38887         if(this.ul){
38888             var li = document.createElement("li");
38889             li.className = "x-menu-list-item";
38890             this.ul.dom.appendChild(li);
38891             item.render(li, this);
38892             this.delayAutoWidth();
38893         }
38894         return item;
38895     },
38896
38897     /**
38898      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38899      * @param {Object} config A MenuItem config object
38900      * @return {Roo.menu.Item} The menu item that was added
38901      */
38902     addMenuItem : function(config){
38903         if(!(config instanceof Roo.menu.Item)){
38904             if(typeof config.checked == "boolean"){ // must be check menu item config?
38905                 config = new Roo.menu.CheckItem(config);
38906             }else{
38907                 config = new Roo.menu.Item(config);
38908             }
38909         }
38910         return this.addItem(config);
38911     },
38912
38913     /**
38914      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38915      * @param {String} text The text to display in the menu item
38916      * @return {Roo.menu.Item} The menu item that was added
38917      */
38918     addText : function(text){
38919         return this.addItem(new Roo.menu.TextItem({ text : text }));
38920     },
38921
38922     /**
38923      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38924      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38925      * @param {Roo.menu.Item} item The menu item to add
38926      * @return {Roo.menu.Item} The menu item that was added
38927      */
38928     insert : function(index, item){
38929         this.items.insert(index, item);
38930         if(this.ul){
38931             var li = document.createElement("li");
38932             li.className = "x-menu-list-item";
38933             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38934             item.render(li, this);
38935             this.delayAutoWidth();
38936         }
38937         return item;
38938     },
38939
38940     /**
38941      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38942      * @param {Roo.menu.Item} item The menu item to remove
38943      */
38944     remove : function(item){
38945         this.items.removeKey(item.id);
38946         item.destroy();
38947     },
38948
38949     /**
38950      * Removes and destroys all items in the menu
38951      */
38952     removeAll : function(){
38953         var f;
38954         while(f = this.items.first()){
38955             this.remove(f);
38956         }
38957     }
38958 });
38959
38960 // MenuNav is a private utility class used internally by the Menu
38961 Roo.menu.MenuNav = function(menu){
38962     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38963     this.scope = this.menu = menu;
38964 };
38965
38966 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38967     doRelay : function(e, h){
38968         var k = e.getKey();
38969         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38970             this.menu.tryActivate(0, 1);
38971             return false;
38972         }
38973         return h.call(this.scope || this, e, this.menu);
38974     },
38975
38976     up : function(e, m){
38977         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38978             m.tryActivate(m.items.length-1, -1);
38979         }
38980     },
38981
38982     down : function(e, m){
38983         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38984             m.tryActivate(0, 1);
38985         }
38986     },
38987
38988     right : function(e, m){
38989         if(m.activeItem){
38990             m.activeItem.expandMenu(true);
38991         }
38992     },
38993
38994     left : function(e, m){
38995         m.hide();
38996         if(m.parentMenu && m.parentMenu.activeItem){
38997             m.parentMenu.activeItem.activate();
38998         }
38999     },
39000
39001     enter : function(e, m){
39002         if(m.activeItem){
39003             e.stopPropagation();
39004             m.activeItem.onClick(e);
39005             m.fireEvent("click", this, m.activeItem);
39006             return true;
39007         }
39008     }
39009 });/*
39010  * Based on:
39011  * Ext JS Library 1.1.1
39012  * Copyright(c) 2006-2007, Ext JS, LLC.
39013  *
39014  * Originally Released Under LGPL - original licence link has changed is not relivant.
39015  *
39016  * Fork - LGPL
39017  * <script type="text/javascript">
39018  */
39019  
39020 /**
39021  * @class Roo.menu.MenuMgr
39022  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39023  * @static
39024  */
39025 Roo.menu.MenuMgr = function(){
39026    var menus, active, groups = {}, attached = false, lastShow = new Date();
39027
39028    // private - called when first menu is created
39029    function init(){
39030        menus = {};
39031        active = new Roo.util.MixedCollection();
39032        Roo.get(document).addKeyListener(27, function(){
39033            if(active.length > 0){
39034                hideAll();
39035            }
39036        });
39037    }
39038
39039    // private
39040    function hideAll(){
39041        if(active && active.length > 0){
39042            var c = active.clone();
39043            c.each(function(m){
39044                m.hide();
39045            });
39046        }
39047    }
39048
39049    // private
39050    function onHide(m){
39051        active.remove(m);
39052        if(active.length < 1){
39053            Roo.get(document).un("mousedown", onMouseDown);
39054            attached = false;
39055        }
39056    }
39057
39058    // private
39059    function onShow(m){
39060        var last = active.last();
39061        lastShow = new Date();
39062        active.add(m);
39063        if(!attached){
39064            Roo.get(document).on("mousedown", onMouseDown);
39065            attached = true;
39066        }
39067        if(m.parentMenu){
39068           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39069           m.parentMenu.activeChild = m;
39070        }else if(last && last.isVisible()){
39071           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39072        }
39073    }
39074
39075    // private
39076    function onBeforeHide(m){
39077        if(m.activeChild){
39078            m.activeChild.hide();
39079        }
39080        if(m.autoHideTimer){
39081            clearTimeout(m.autoHideTimer);
39082            delete m.autoHideTimer;
39083        }
39084    }
39085
39086    // private
39087    function onBeforeShow(m){
39088        var pm = m.parentMenu;
39089        if(!pm && !m.allowOtherMenus){
39090            hideAll();
39091        }else if(pm && pm.activeChild && active != m){
39092            pm.activeChild.hide();
39093        }
39094    }
39095
39096    // private
39097    function onMouseDown(e){
39098        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39099            hideAll();
39100        }
39101    }
39102
39103    // private
39104    function onBeforeCheck(mi, state){
39105        if(state){
39106            var g = groups[mi.group];
39107            for(var i = 0, l = g.length; i < l; i++){
39108                if(g[i] != mi){
39109                    g[i].setChecked(false);
39110                }
39111            }
39112        }
39113    }
39114
39115    return {
39116
39117        /**
39118         * Hides all menus that are currently visible
39119         */
39120        hideAll : function(){
39121             hideAll();  
39122        },
39123
39124        // private
39125        register : function(menu){
39126            if(!menus){
39127                init();
39128            }
39129            menus[menu.id] = menu;
39130            menu.on("beforehide", onBeforeHide);
39131            menu.on("hide", onHide);
39132            menu.on("beforeshow", onBeforeShow);
39133            menu.on("show", onShow);
39134            var g = menu.group;
39135            if(g && menu.events["checkchange"]){
39136                if(!groups[g]){
39137                    groups[g] = [];
39138                }
39139                groups[g].push(menu);
39140                menu.on("checkchange", onCheck);
39141            }
39142        },
39143
39144         /**
39145          * Returns a {@link Roo.menu.Menu} object
39146          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39147          * be used to generate and return a new Menu instance.
39148          */
39149        get : function(menu){
39150            if(typeof menu == "string"){ // menu id
39151                return menus[menu];
39152            }else if(menu.events){  // menu instance
39153                return menu;
39154            }else if(typeof menu.length == 'number'){ // array of menu items?
39155                return new Roo.menu.Menu({items:menu});
39156            }else{ // otherwise, must be a config
39157                return new Roo.menu.Menu(menu);
39158            }
39159        },
39160
39161        // private
39162        unregister : function(menu){
39163            delete menus[menu.id];
39164            menu.un("beforehide", onBeforeHide);
39165            menu.un("hide", onHide);
39166            menu.un("beforeshow", onBeforeShow);
39167            menu.un("show", onShow);
39168            var g = menu.group;
39169            if(g && menu.events["checkchange"]){
39170                groups[g].remove(menu);
39171                menu.un("checkchange", onCheck);
39172            }
39173        },
39174
39175        // private
39176        registerCheckable : function(menuItem){
39177            var g = menuItem.group;
39178            if(g){
39179                if(!groups[g]){
39180                    groups[g] = [];
39181                }
39182                groups[g].push(menuItem);
39183                menuItem.on("beforecheckchange", onBeforeCheck);
39184            }
39185        },
39186
39187        // private
39188        unregisterCheckable : function(menuItem){
39189            var g = menuItem.group;
39190            if(g){
39191                groups[g].remove(menuItem);
39192                menuItem.un("beforecheckchange", onBeforeCheck);
39193            }
39194        }
39195    };
39196 }();/*
39197  * Based on:
39198  * Ext JS Library 1.1.1
39199  * Copyright(c) 2006-2007, Ext JS, LLC.
39200  *
39201  * Originally Released Under LGPL - original licence link has changed is not relivant.
39202  *
39203  * Fork - LGPL
39204  * <script type="text/javascript">
39205  */
39206  
39207
39208 /**
39209  * @class Roo.menu.BaseItem
39210  * @extends Roo.Component
39211  * @abstract
39212  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39213  * management and base configuration options shared by all menu components.
39214  * @constructor
39215  * Creates a new BaseItem
39216  * @param {Object} config Configuration options
39217  */
39218 Roo.menu.BaseItem = function(config){
39219     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39220
39221     this.addEvents({
39222         /**
39223          * @event click
39224          * Fires when this item is clicked
39225          * @param {Roo.menu.BaseItem} this
39226          * @param {Roo.EventObject} e
39227          */
39228         click: true,
39229         /**
39230          * @event activate
39231          * Fires when this item is activated
39232          * @param {Roo.menu.BaseItem} this
39233          */
39234         activate : true,
39235         /**
39236          * @event deactivate
39237          * Fires when this item is deactivated
39238          * @param {Roo.menu.BaseItem} this
39239          */
39240         deactivate : true
39241     });
39242
39243     if(this.handler){
39244         this.on("click", this.handler, this.scope, true);
39245     }
39246 };
39247
39248 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39249     /**
39250      * @cfg {Function} handler
39251      * A function that will handle the click event of this menu item (defaults to undefined)
39252      */
39253     /**
39254      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39255      */
39256     canActivate : false,
39257     
39258      /**
39259      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39260      */
39261     hidden: false,
39262     
39263     /**
39264      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39265      */
39266     activeClass : "x-menu-item-active",
39267     /**
39268      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39269      */
39270     hideOnClick : true,
39271     /**
39272      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39273      */
39274     hideDelay : 100,
39275
39276     // private
39277     ctype: "Roo.menu.BaseItem",
39278
39279     // private
39280     actionMode : "container",
39281
39282     // private
39283     render : function(container, parentMenu){
39284         this.parentMenu = parentMenu;
39285         Roo.menu.BaseItem.superclass.render.call(this, container);
39286         this.container.menuItemId = this.id;
39287     },
39288
39289     // private
39290     onRender : function(container, position){
39291         this.el = Roo.get(this.el);
39292         container.dom.appendChild(this.el.dom);
39293     },
39294
39295     // private
39296     onClick : function(e){
39297         if(!this.disabled && this.fireEvent("click", this, e) !== false
39298                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39299             this.handleClick(e);
39300         }else{
39301             e.stopEvent();
39302         }
39303     },
39304
39305     // private
39306     activate : function(){
39307         if(this.disabled){
39308             return false;
39309         }
39310         var li = this.container;
39311         li.addClass(this.activeClass);
39312         this.region = li.getRegion().adjust(2, 2, -2, -2);
39313         this.fireEvent("activate", this);
39314         return true;
39315     },
39316
39317     // private
39318     deactivate : function(){
39319         this.container.removeClass(this.activeClass);
39320         this.fireEvent("deactivate", this);
39321     },
39322
39323     // private
39324     shouldDeactivate : function(e){
39325         return !this.region || !this.region.contains(e.getPoint());
39326     },
39327
39328     // private
39329     handleClick : function(e){
39330         if(this.hideOnClick){
39331             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39332         }
39333     },
39334
39335     // private
39336     expandMenu : function(autoActivate){
39337         // do nothing
39338     },
39339
39340     // private
39341     hideMenu : function(){
39342         // do nothing
39343     }
39344 });/*
39345  * Based on:
39346  * Ext JS Library 1.1.1
39347  * Copyright(c) 2006-2007, Ext JS, LLC.
39348  *
39349  * Originally Released Under LGPL - original licence link has changed is not relivant.
39350  *
39351  * Fork - LGPL
39352  * <script type="text/javascript">
39353  */
39354  
39355 /**
39356  * @class Roo.menu.Adapter
39357  * @extends Roo.menu.BaseItem
39358  * @abstract
39359  * 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.
39360  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39361  * @constructor
39362  * Creates a new Adapter
39363  * @param {Object} config Configuration options
39364  */
39365 Roo.menu.Adapter = function(component, config){
39366     Roo.menu.Adapter.superclass.constructor.call(this, config);
39367     this.component = component;
39368 };
39369 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39370     // private
39371     canActivate : true,
39372
39373     // private
39374     onRender : function(container, position){
39375         this.component.render(container);
39376         this.el = this.component.getEl();
39377     },
39378
39379     // private
39380     activate : function(){
39381         if(this.disabled){
39382             return false;
39383         }
39384         this.component.focus();
39385         this.fireEvent("activate", this);
39386         return true;
39387     },
39388
39389     // private
39390     deactivate : function(){
39391         this.fireEvent("deactivate", this);
39392     },
39393
39394     // private
39395     disable : function(){
39396         this.component.disable();
39397         Roo.menu.Adapter.superclass.disable.call(this);
39398     },
39399
39400     // private
39401     enable : function(){
39402         this.component.enable();
39403         Roo.menu.Adapter.superclass.enable.call(this);
39404     }
39405 });/*
39406  * Based on:
39407  * Ext JS Library 1.1.1
39408  * Copyright(c) 2006-2007, Ext JS, LLC.
39409  *
39410  * Originally Released Under LGPL - original licence link has changed is not relivant.
39411  *
39412  * Fork - LGPL
39413  * <script type="text/javascript">
39414  */
39415
39416 /**
39417  * @class Roo.menu.TextItem
39418  * @extends Roo.menu.BaseItem
39419  * Adds a static text string to a menu, usually used as either a heading or group separator.
39420  * Note: old style constructor with text is still supported.
39421  * 
39422  * @constructor
39423  * Creates a new TextItem
39424  * @param {Object} cfg Configuration
39425  */
39426 Roo.menu.TextItem = function(cfg){
39427     if (typeof(cfg) == 'string') {
39428         this.text = cfg;
39429     } else {
39430         Roo.apply(this,cfg);
39431     }
39432     
39433     Roo.menu.TextItem.superclass.constructor.call(this);
39434 };
39435
39436 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39437     /**
39438      * @cfg {String} text Text to show on item.
39439      */
39440     text : '',
39441     
39442     /**
39443      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39444      */
39445     hideOnClick : false,
39446     /**
39447      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39448      */
39449     itemCls : "x-menu-text",
39450
39451     // private
39452     onRender : function(){
39453         var s = document.createElement("span");
39454         s.className = this.itemCls;
39455         s.innerHTML = this.text;
39456         this.el = s;
39457         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39458     }
39459 });/*
39460  * Based on:
39461  * Ext JS Library 1.1.1
39462  * Copyright(c) 2006-2007, Ext JS, LLC.
39463  *
39464  * Originally Released Under LGPL - original licence link has changed is not relivant.
39465  *
39466  * Fork - LGPL
39467  * <script type="text/javascript">
39468  */
39469
39470 /**
39471  * @class Roo.menu.Separator
39472  * @extends Roo.menu.BaseItem
39473  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39474  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39475  * @constructor
39476  * @param {Object} config Configuration options
39477  */
39478 Roo.menu.Separator = function(config){
39479     Roo.menu.Separator.superclass.constructor.call(this, config);
39480 };
39481
39482 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39483     /**
39484      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39485      */
39486     itemCls : "x-menu-sep",
39487     /**
39488      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39489      */
39490     hideOnClick : false,
39491
39492     // private
39493     onRender : function(li){
39494         var s = document.createElement("span");
39495         s.className = this.itemCls;
39496         s.innerHTML = "&#160;";
39497         this.el = s;
39498         li.addClass("x-menu-sep-li");
39499         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39500     }
39501 });/*
39502  * Based on:
39503  * Ext JS Library 1.1.1
39504  * Copyright(c) 2006-2007, Ext JS, LLC.
39505  *
39506  * Originally Released Under LGPL - original licence link has changed is not relivant.
39507  *
39508  * Fork - LGPL
39509  * <script type="text/javascript">
39510  */
39511 /**
39512  * @class Roo.menu.Item
39513  * @extends Roo.menu.BaseItem
39514  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39515  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39516  * activation and click handling.
39517  * @constructor
39518  * Creates a new Item
39519  * @param {Object} config Configuration options
39520  */
39521 Roo.menu.Item = function(config){
39522     Roo.menu.Item.superclass.constructor.call(this, config);
39523     if(this.menu){
39524         this.menu = Roo.menu.MenuMgr.get(this.menu);
39525     }
39526 };
39527 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39528     /**
39529      * @cfg {Roo.menu.Menu} menu
39530      * A Sub menu
39531      */
39532     /**
39533      * @cfg {String} text
39534      * The text to show on the menu item.
39535      */
39536     text: '',
39537      /**
39538      * @cfg {String} html to render in menu
39539      * The text to show on the menu item (HTML version).
39540      */
39541     html: '',
39542     /**
39543      * @cfg {String} icon
39544      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39545      */
39546     icon: undefined,
39547     /**
39548      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39549      */
39550     itemCls : "x-menu-item",
39551     /**
39552      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39553      */
39554     canActivate : true,
39555     /**
39556      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39557      */
39558     showDelay: 200,
39559     // doc'd in BaseItem
39560     hideDelay: 200,
39561
39562     // private
39563     ctype: "Roo.menu.Item",
39564     
39565     // private
39566     onRender : function(container, position){
39567         var el = document.createElement("a");
39568         el.hideFocus = true;
39569         el.unselectable = "on";
39570         el.href = this.href || "#";
39571         if(this.hrefTarget){
39572             el.target = this.hrefTarget;
39573         }
39574         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39575         
39576         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39577         
39578         el.innerHTML = String.format(
39579                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39580                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39581         this.el = el;
39582         Roo.menu.Item.superclass.onRender.call(this, container, position);
39583     },
39584
39585     /**
39586      * Sets the text to display in this menu item
39587      * @param {String} text The text to display
39588      * @param {Boolean} isHTML true to indicate text is pure html.
39589      */
39590     setText : function(text, isHTML){
39591         if (isHTML) {
39592             this.html = text;
39593         } else {
39594             this.text = text;
39595             this.html = '';
39596         }
39597         if(this.rendered){
39598             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39599      
39600             this.el.update(String.format(
39601                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39602                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39603             this.parentMenu.autoWidth();
39604         }
39605     },
39606
39607     // private
39608     handleClick : function(e){
39609         if(!this.href){ // if no link defined, stop the event automatically
39610             e.stopEvent();
39611         }
39612         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39613     },
39614
39615     // private
39616     activate : function(autoExpand){
39617         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39618             this.focus();
39619             if(autoExpand){
39620                 this.expandMenu();
39621             }
39622         }
39623         return true;
39624     },
39625
39626     // private
39627     shouldDeactivate : function(e){
39628         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39629             if(this.menu && this.menu.isVisible()){
39630                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39631             }
39632             return true;
39633         }
39634         return false;
39635     },
39636
39637     // private
39638     deactivate : function(){
39639         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39640         this.hideMenu();
39641     },
39642
39643     // private
39644     expandMenu : function(autoActivate){
39645         if(!this.disabled && this.menu){
39646             clearTimeout(this.hideTimer);
39647             delete this.hideTimer;
39648             if(!this.menu.isVisible() && !this.showTimer){
39649                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39650             }else if (this.menu.isVisible() && autoActivate){
39651                 this.menu.tryActivate(0, 1);
39652             }
39653         }
39654     },
39655
39656     // private
39657     deferExpand : function(autoActivate){
39658         delete this.showTimer;
39659         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39660         if(autoActivate){
39661             this.menu.tryActivate(0, 1);
39662         }
39663     },
39664
39665     // private
39666     hideMenu : function(){
39667         clearTimeout(this.showTimer);
39668         delete this.showTimer;
39669         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39670             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39671         }
39672     },
39673
39674     // private
39675     deferHide : function(){
39676         delete this.hideTimer;
39677         this.menu.hide();
39678     }
39679 });/*
39680  * Based on:
39681  * Ext JS Library 1.1.1
39682  * Copyright(c) 2006-2007, Ext JS, LLC.
39683  *
39684  * Originally Released Under LGPL - original licence link has changed is not relivant.
39685  *
39686  * Fork - LGPL
39687  * <script type="text/javascript">
39688  */
39689  
39690 /**
39691  * @class Roo.menu.CheckItem
39692  * @extends Roo.menu.Item
39693  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39694  * @constructor
39695  * Creates a new CheckItem
39696  * @param {Object} config Configuration options
39697  */
39698 Roo.menu.CheckItem = function(config){
39699     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39700     this.addEvents({
39701         /**
39702          * @event beforecheckchange
39703          * Fires before the checked value is set, providing an opportunity to cancel if needed
39704          * @param {Roo.menu.CheckItem} this
39705          * @param {Boolean} checked The new checked value that will be set
39706          */
39707         "beforecheckchange" : true,
39708         /**
39709          * @event checkchange
39710          * Fires after the checked value has been set
39711          * @param {Roo.menu.CheckItem} this
39712          * @param {Boolean} checked The checked value that was set
39713          */
39714         "checkchange" : true
39715     });
39716     if(this.checkHandler){
39717         this.on('checkchange', this.checkHandler, this.scope);
39718     }
39719 };
39720 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39721     /**
39722      * @cfg {String} group
39723      * All check items with the same group name will automatically be grouped into a single-select
39724      * radio button group (defaults to '')
39725      */
39726     /**
39727      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39728      */
39729     itemCls : "x-menu-item x-menu-check-item",
39730     /**
39731      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39732      */
39733     groupClass : "x-menu-group-item",
39734
39735     /**
39736      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39737      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39738      * initialized with checked = true will be rendered as checked.
39739      */
39740     checked: false,
39741
39742     // private
39743     ctype: "Roo.menu.CheckItem",
39744
39745     // private
39746     onRender : function(c){
39747         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39748         if(this.group){
39749             this.el.addClass(this.groupClass);
39750         }
39751         Roo.menu.MenuMgr.registerCheckable(this);
39752         if(this.checked){
39753             this.checked = false;
39754             this.setChecked(true, true);
39755         }
39756     },
39757
39758     // private
39759     destroy : function(){
39760         if(this.rendered){
39761             Roo.menu.MenuMgr.unregisterCheckable(this);
39762         }
39763         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39764     },
39765
39766     /**
39767      * Set the checked state of this item
39768      * @param {Boolean} checked The new checked value
39769      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39770      */
39771     setChecked : function(state, suppressEvent){
39772         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39773             if(this.container){
39774                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39775             }
39776             this.checked = state;
39777             if(suppressEvent !== true){
39778                 this.fireEvent("checkchange", this, state);
39779             }
39780         }
39781     },
39782
39783     // private
39784     handleClick : function(e){
39785        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39786            this.setChecked(!this.checked);
39787        }
39788        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39789     }
39790 });/*
39791  * Based on:
39792  * Ext JS Library 1.1.1
39793  * Copyright(c) 2006-2007, Ext JS, LLC.
39794  *
39795  * Originally Released Under LGPL - original licence link has changed is not relivant.
39796  *
39797  * Fork - LGPL
39798  * <script type="text/javascript">
39799  */
39800  
39801 /**
39802  * @class Roo.menu.DateItem
39803  * @extends Roo.menu.Adapter
39804  * A menu item that wraps the {@link Roo.DatPicker} component.
39805  * @constructor
39806  * Creates a new DateItem
39807  * @param {Object} config Configuration options
39808  */
39809 Roo.menu.DateItem = function(config){
39810     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39811     /** The Roo.DatePicker object @type Roo.DatePicker */
39812     this.picker = this.component;
39813     this.addEvents({select: true});
39814     
39815     this.picker.on("render", function(picker){
39816         picker.getEl().swallowEvent("click");
39817         picker.container.addClass("x-menu-date-item");
39818     });
39819
39820     this.picker.on("select", this.onSelect, this);
39821 };
39822
39823 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39824     // private
39825     onSelect : function(picker, date){
39826         this.fireEvent("select", this, date, picker);
39827         Roo.menu.DateItem.superclass.handleClick.call(this);
39828     }
39829 });/*
39830  * Based on:
39831  * Ext JS Library 1.1.1
39832  * Copyright(c) 2006-2007, Ext JS, LLC.
39833  *
39834  * Originally Released Under LGPL - original licence link has changed is not relivant.
39835  *
39836  * Fork - LGPL
39837  * <script type="text/javascript">
39838  */
39839  
39840 /**
39841  * @class Roo.menu.ColorItem
39842  * @extends Roo.menu.Adapter
39843  * A menu item that wraps the {@link Roo.ColorPalette} component.
39844  * @constructor
39845  * Creates a new ColorItem
39846  * @param {Object} config Configuration options
39847  */
39848 Roo.menu.ColorItem = function(config){
39849     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39850     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39851     this.palette = this.component;
39852     this.relayEvents(this.palette, ["select"]);
39853     if(this.selectHandler){
39854         this.on('select', this.selectHandler, this.scope);
39855     }
39856 };
39857 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39858  * Based on:
39859  * Ext JS Library 1.1.1
39860  * Copyright(c) 2006-2007, Ext JS, LLC.
39861  *
39862  * Originally Released Under LGPL - original licence link has changed is not relivant.
39863  *
39864  * Fork - LGPL
39865  * <script type="text/javascript">
39866  */
39867  
39868
39869 /**
39870  * @class Roo.menu.DateMenu
39871  * @extends Roo.menu.Menu
39872  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39873  * @constructor
39874  * Creates a new DateMenu
39875  * @param {Object} config Configuration options
39876  */
39877 Roo.menu.DateMenu = function(config){
39878     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39879     this.plain = true;
39880     var di = new Roo.menu.DateItem(config);
39881     this.add(di);
39882     /**
39883      * The {@link Roo.DatePicker} instance for this DateMenu
39884      * @type DatePicker
39885      */
39886     this.picker = di.picker;
39887     /**
39888      * @event select
39889      * @param {DatePicker} picker
39890      * @param {Date} date
39891      */
39892     this.relayEvents(di, ["select"]);
39893     this.on('beforeshow', function(){
39894         if(this.picker){
39895             this.picker.hideMonthPicker(false);
39896         }
39897     }, this);
39898 };
39899 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39900     cls:'x-date-menu'
39901 });/*
39902  * Based on:
39903  * Ext JS Library 1.1.1
39904  * Copyright(c) 2006-2007, Ext JS, LLC.
39905  *
39906  * Originally Released Under LGPL - original licence link has changed is not relivant.
39907  *
39908  * Fork - LGPL
39909  * <script type="text/javascript">
39910  */
39911  
39912
39913 /**
39914  * @class Roo.menu.ColorMenu
39915  * @extends Roo.menu.Menu
39916  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39917  * @constructor
39918  * Creates a new ColorMenu
39919  * @param {Object} config Configuration options
39920  */
39921 Roo.menu.ColorMenu = function(config){
39922     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39923     this.plain = true;
39924     var ci = new Roo.menu.ColorItem(config);
39925     this.add(ci);
39926     /**
39927      * The {@link Roo.ColorPalette} instance for this ColorMenu
39928      * @type ColorPalette
39929      */
39930     this.palette = ci.palette;
39931     /**
39932      * @event select
39933      * @param {ColorPalette} palette
39934      * @param {String} color
39935      */
39936     this.relayEvents(ci, ["select"]);
39937 };
39938 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39939  * Based on:
39940  * Ext JS Library 1.1.1
39941  * Copyright(c) 2006-2007, Ext JS, LLC.
39942  *
39943  * Originally Released Under LGPL - original licence link has changed is not relivant.
39944  *
39945  * Fork - LGPL
39946  * <script type="text/javascript">
39947  */
39948  
39949 /**
39950  * @class Roo.form.TextItem
39951  * @extends Roo.BoxComponent
39952  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39953  * @constructor
39954  * Creates a new TextItem
39955  * @param {Object} config Configuration options
39956  */
39957 Roo.form.TextItem = function(config){
39958     Roo.form.TextItem.superclass.constructor.call(this, config);
39959 };
39960
39961 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39962     
39963     /**
39964      * @cfg {String} tag the tag for this item (default div)
39965      */
39966     tag : 'div',
39967     /**
39968      * @cfg {String} html the content for this item
39969      */
39970     html : '',
39971     
39972     getAutoCreate : function()
39973     {
39974         var cfg = {
39975             id: this.id,
39976             tag: this.tag,
39977             html: this.html,
39978             cls: 'x-form-item'
39979         };
39980         
39981         return cfg;
39982         
39983     },
39984     
39985     onRender : function(ct, position)
39986     {
39987         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39988         
39989         if(!this.el){
39990             var cfg = this.getAutoCreate();
39991             if(!cfg.name){
39992                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39993             }
39994             if (!cfg.name.length) {
39995                 delete cfg.name;
39996             }
39997             this.el = ct.createChild(cfg, position);
39998         }
39999     },
40000     /*
40001      * setHTML
40002      * @param {String} html update the Contents of the element.
40003      */
40004     setHTML : function(html)
40005     {
40006         this.fieldEl.dom.innerHTML = html;
40007     }
40008     
40009 });/*
40010  * Based on:
40011  * Ext JS Library 1.1.1
40012  * Copyright(c) 2006-2007, Ext JS, LLC.
40013  *
40014  * Originally Released Under LGPL - original licence link has changed is not relivant.
40015  *
40016  * Fork - LGPL
40017  * <script type="text/javascript">
40018  */
40019  
40020 /**
40021  * @class Roo.form.Field
40022  * @extends Roo.BoxComponent
40023  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40024  * @constructor
40025  * Creates a new Field
40026  * @param {Object} config Configuration options
40027  */
40028 Roo.form.Field = function(config){
40029     Roo.form.Field.superclass.constructor.call(this, config);
40030 };
40031
40032 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40033     /**
40034      * @cfg {String} fieldLabel Label to use when rendering a form.
40035      */
40036        /**
40037      * @cfg {String} qtip Mouse over tip
40038      */
40039      
40040     /**
40041      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40042      */
40043     invalidClass : "x-form-invalid",
40044     /**
40045      * @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")
40046      */
40047     invalidText : "The value in this field is invalid",
40048     /**
40049      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40050      */
40051     focusClass : "x-form-focus",
40052     /**
40053      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40054       automatic validation (defaults to "keyup").
40055      */
40056     validationEvent : "keyup",
40057     /**
40058      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40059      */
40060     validateOnBlur : true,
40061     /**
40062      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40063      */
40064     validationDelay : 250,
40065     /**
40066      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40067      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40068      */
40069     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40070     /**
40071      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40072      */
40073     fieldClass : "x-form-field",
40074     /**
40075      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40076      *<pre>
40077 Value         Description
40078 -----------   ----------------------------------------------------------------------
40079 qtip          Display a quick tip when the user hovers over the field
40080 title         Display a default browser title attribute popup
40081 under         Add a block div beneath the field containing the error text
40082 side          Add an error icon to the right of the field with a popup on hover
40083 [element id]  Add the error text directly to the innerHTML of the specified element
40084 </pre>
40085      */
40086     msgTarget : 'qtip',
40087     /**
40088      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40089      */
40090     msgFx : 'normal',
40091
40092     /**
40093      * @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.
40094      */
40095     readOnly : false,
40096
40097     /**
40098      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40099      */
40100     disabled : false,
40101
40102     /**
40103      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40104      */
40105     inputType : undefined,
40106     
40107     /**
40108      * @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).
40109          */
40110         tabIndex : undefined,
40111         
40112     // private
40113     isFormField : true,
40114
40115     // private
40116     hasFocus : false,
40117     /**
40118      * @property {Roo.Element} fieldEl
40119      * Element Containing the rendered Field (with label etc.)
40120      */
40121     /**
40122      * @cfg {Mixed} value A value to initialize this field with.
40123      */
40124     value : undefined,
40125
40126     /**
40127      * @cfg {String} name The field's HTML name attribute.
40128      */
40129     /**
40130      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40131      */
40132     // private
40133     loadedValue : false,
40134      
40135      
40136         // private ??
40137         initComponent : function(){
40138         Roo.form.Field.superclass.initComponent.call(this);
40139         this.addEvents({
40140             /**
40141              * @event focus
40142              * Fires when this field receives input focus.
40143              * @param {Roo.form.Field} this
40144              */
40145             focus : true,
40146             /**
40147              * @event blur
40148              * Fires when this field loses input focus.
40149              * @param {Roo.form.Field} this
40150              */
40151             blur : true,
40152             /**
40153              * @event specialkey
40154              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40155              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40156              * @param {Roo.form.Field} this
40157              * @param {Roo.EventObject} e The event object
40158              */
40159             specialkey : true,
40160             /**
40161              * @event change
40162              * Fires just before the field blurs if the field value has changed.
40163              * @param {Roo.form.Field} this
40164              * @param {Mixed} newValue The new value
40165              * @param {Mixed} oldValue The original value
40166              */
40167             change : true,
40168             /**
40169              * @event invalid
40170              * Fires after the field has been marked as invalid.
40171              * @param {Roo.form.Field} this
40172              * @param {String} msg The validation message
40173              */
40174             invalid : true,
40175             /**
40176              * @event valid
40177              * Fires after the field has been validated with no errors.
40178              * @param {Roo.form.Field} this
40179              */
40180             valid : true,
40181              /**
40182              * @event keyup
40183              * Fires after the key up
40184              * @param {Roo.form.Field} this
40185              * @param {Roo.EventObject}  e The event Object
40186              */
40187             keyup : true
40188         });
40189     },
40190
40191     /**
40192      * Returns the name attribute of the field if available
40193      * @return {String} name The field name
40194      */
40195     getName: function(){
40196          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40197     },
40198
40199     // private
40200     onRender : function(ct, position){
40201         Roo.form.Field.superclass.onRender.call(this, ct, position);
40202         if(!this.el){
40203             var cfg = this.getAutoCreate();
40204             if(!cfg.name){
40205                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40206             }
40207             if (!cfg.name.length) {
40208                 delete cfg.name;
40209             }
40210             if(this.inputType){
40211                 cfg.type = this.inputType;
40212             }
40213             this.el = ct.createChild(cfg, position);
40214         }
40215         var type = this.el.dom.type;
40216         if(type){
40217             if(type == 'password'){
40218                 type = 'text';
40219             }
40220             this.el.addClass('x-form-'+type);
40221         }
40222         if(this.readOnly){
40223             this.el.dom.readOnly = true;
40224         }
40225         if(this.tabIndex !== undefined){
40226             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40227         }
40228
40229         this.el.addClass([this.fieldClass, this.cls]);
40230         this.initValue();
40231     },
40232
40233     /**
40234      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40235      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40236      * @return {Roo.form.Field} this
40237      */
40238     applyTo : function(target){
40239         this.allowDomMove = false;
40240         this.el = Roo.get(target);
40241         this.render(this.el.dom.parentNode);
40242         return this;
40243     },
40244
40245     // private
40246     initValue : function(){
40247         if(this.value !== undefined){
40248             this.setValue(this.value);
40249         }else if(this.el.dom.value.length > 0){
40250             this.setValue(this.el.dom.value);
40251         }
40252     },
40253
40254     /**
40255      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40256      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40257      */
40258     isDirty : function() {
40259         if(this.disabled) {
40260             return false;
40261         }
40262         return String(this.getValue()) !== String(this.originalValue);
40263     },
40264
40265     /**
40266      * stores the current value in loadedValue
40267      */
40268     resetHasChanged : function()
40269     {
40270         this.loadedValue = String(this.getValue());
40271     },
40272     /**
40273      * checks the current value against the 'loaded' value.
40274      * Note - will return false if 'resetHasChanged' has not been called first.
40275      */
40276     hasChanged : function()
40277     {
40278         if(this.disabled || this.readOnly) {
40279             return false;
40280         }
40281         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40282     },
40283     
40284     
40285     
40286     // private
40287     afterRender : function(){
40288         Roo.form.Field.superclass.afterRender.call(this);
40289         this.initEvents();
40290     },
40291
40292     // private
40293     fireKey : function(e){
40294         //Roo.log('field ' + e.getKey());
40295         if(e.isNavKeyPress()){
40296             this.fireEvent("specialkey", this, e);
40297         }
40298     },
40299
40300     /**
40301      * Resets the current field value to the originally loaded value and clears any validation messages
40302      */
40303     reset : function(){
40304         this.setValue(this.resetValue);
40305         this.originalValue = this.getValue();
40306         this.clearInvalid();
40307     },
40308
40309     // private
40310     initEvents : function(){
40311         // safari killled keypress - so keydown is now used..
40312         this.el.on("keydown" , this.fireKey,  this);
40313         this.el.on("focus", this.onFocus,  this);
40314         this.el.on("blur", this.onBlur,  this);
40315         this.el.relayEvent('keyup', this);
40316
40317         // reference to original value for reset
40318         this.originalValue = this.getValue();
40319         this.resetValue =  this.getValue();
40320     },
40321
40322     // private
40323     onFocus : function(){
40324         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40325             this.el.addClass(this.focusClass);
40326         }
40327         if(!this.hasFocus){
40328             this.hasFocus = true;
40329             this.startValue = this.getValue();
40330             this.fireEvent("focus", this);
40331         }
40332     },
40333
40334     beforeBlur : Roo.emptyFn,
40335
40336     // private
40337     onBlur : function(){
40338         this.beforeBlur();
40339         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40340             this.el.removeClass(this.focusClass);
40341         }
40342         this.hasFocus = false;
40343         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40344             this.validate();
40345         }
40346         var v = this.getValue();
40347         if(String(v) !== String(this.startValue)){
40348             this.fireEvent('change', this, v, this.startValue);
40349         }
40350         this.fireEvent("blur", this);
40351     },
40352
40353     /**
40354      * Returns whether or not the field value is currently valid
40355      * @param {Boolean} preventMark True to disable marking the field invalid
40356      * @return {Boolean} True if the value is valid, else false
40357      */
40358     isValid : function(preventMark){
40359         if(this.disabled){
40360             return true;
40361         }
40362         var restore = this.preventMark;
40363         this.preventMark = preventMark === true;
40364         var v = this.validateValue(this.processValue(this.getRawValue()));
40365         this.preventMark = restore;
40366         return v;
40367     },
40368
40369     /**
40370      * Validates the field value
40371      * @return {Boolean} True if the value is valid, else false
40372      */
40373     validate : function(){
40374         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40375             this.clearInvalid();
40376             return true;
40377         }
40378         return false;
40379     },
40380
40381     processValue : function(value){
40382         return value;
40383     },
40384
40385     // private
40386     // Subclasses should provide the validation implementation by overriding this
40387     validateValue : function(value){
40388         return true;
40389     },
40390
40391     /**
40392      * Mark this field as invalid
40393      * @param {String} msg The validation message
40394      */
40395     markInvalid : function(msg){
40396         if(!this.rendered || this.preventMark){ // not rendered
40397             return;
40398         }
40399         
40400         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40401         
40402         obj.el.addClass(this.invalidClass);
40403         msg = msg || this.invalidText;
40404         switch(this.msgTarget){
40405             case 'qtip':
40406                 obj.el.dom.qtip = msg;
40407                 obj.el.dom.qclass = 'x-form-invalid-tip';
40408                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40409                     Roo.QuickTips.enable();
40410                 }
40411                 break;
40412             case 'title':
40413                 this.el.dom.title = msg;
40414                 break;
40415             case 'under':
40416                 if(!this.errorEl){
40417                     var elp = this.el.findParent('.x-form-element', 5, true);
40418                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40419                     this.errorEl.setWidth(elp.getWidth(true)-20);
40420                 }
40421                 this.errorEl.update(msg);
40422                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40423                 break;
40424             case 'side':
40425                 if(!this.errorIcon){
40426                     var elp = this.el.findParent('.x-form-element', 5, true);
40427                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40428                 }
40429                 this.alignErrorIcon();
40430                 this.errorIcon.dom.qtip = msg;
40431                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40432                 this.errorIcon.show();
40433                 this.on('resize', this.alignErrorIcon, this);
40434                 break;
40435             default:
40436                 var t = Roo.getDom(this.msgTarget);
40437                 t.innerHTML = msg;
40438                 t.style.display = this.msgDisplay;
40439                 break;
40440         }
40441         this.fireEvent('invalid', this, msg);
40442     },
40443
40444     // private
40445     alignErrorIcon : function(){
40446         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40447     },
40448
40449     /**
40450      * Clear any invalid styles/messages for this field
40451      */
40452     clearInvalid : function(){
40453         if(!this.rendered || this.preventMark){ // not rendered
40454             return;
40455         }
40456         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40457         
40458         obj.el.removeClass(this.invalidClass);
40459         switch(this.msgTarget){
40460             case 'qtip':
40461                 obj.el.dom.qtip = '';
40462                 break;
40463             case 'title':
40464                 this.el.dom.title = '';
40465                 break;
40466             case 'under':
40467                 if(this.errorEl){
40468                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40469                 }
40470                 break;
40471             case 'side':
40472                 if(this.errorIcon){
40473                     this.errorIcon.dom.qtip = '';
40474                     this.errorIcon.hide();
40475                     this.un('resize', this.alignErrorIcon, this);
40476                 }
40477                 break;
40478             default:
40479                 var t = Roo.getDom(this.msgTarget);
40480                 t.innerHTML = '';
40481                 t.style.display = 'none';
40482                 break;
40483         }
40484         this.fireEvent('valid', this);
40485     },
40486
40487     /**
40488      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40489      * @return {Mixed} value The field value
40490      */
40491     getRawValue : function(){
40492         var v = this.el.getValue();
40493         
40494         return v;
40495     },
40496
40497     /**
40498      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40499      * @return {Mixed} value The field value
40500      */
40501     getValue : function(){
40502         var v = this.el.getValue();
40503          
40504         return v;
40505     },
40506
40507     /**
40508      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40509      * @param {Mixed} value The value to set
40510      */
40511     setRawValue : function(v){
40512         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40513     },
40514
40515     /**
40516      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40517      * @param {Mixed} value The value to set
40518      */
40519     setValue : function(v){
40520         this.value = v;
40521         if(this.rendered){
40522             this.el.dom.value = (v === null || v === undefined ? '' : v);
40523              this.validate();
40524         }
40525     },
40526
40527     adjustSize : function(w, h){
40528         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40529         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40530         return s;
40531     },
40532
40533     adjustWidth : function(tag, w){
40534         tag = tag.toLowerCase();
40535         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40536             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40537                 if(tag == 'input'){
40538                     return w + 2;
40539                 }
40540                 if(tag == 'textarea'){
40541                     return w-2;
40542                 }
40543             }else if(Roo.isOpera){
40544                 if(tag == 'input'){
40545                     return w + 2;
40546                 }
40547                 if(tag == 'textarea'){
40548                     return w-2;
40549                 }
40550             }
40551         }
40552         return w;
40553     }
40554 });
40555
40556
40557 // anything other than normal should be considered experimental
40558 Roo.form.Field.msgFx = {
40559     normal : {
40560         show: function(msgEl, f){
40561             msgEl.setDisplayed('block');
40562         },
40563
40564         hide : function(msgEl, f){
40565             msgEl.setDisplayed(false).update('');
40566         }
40567     },
40568
40569     slide : {
40570         show: function(msgEl, f){
40571             msgEl.slideIn('t', {stopFx:true});
40572         },
40573
40574         hide : function(msgEl, f){
40575             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40576         }
40577     },
40578
40579     slideRight : {
40580         show: function(msgEl, f){
40581             msgEl.fixDisplay();
40582             msgEl.alignTo(f.el, 'tl-tr');
40583             msgEl.slideIn('l', {stopFx:true});
40584         },
40585
40586         hide : function(msgEl, f){
40587             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40588         }
40589     }
40590 };/*
40591  * Based on:
40592  * Ext JS Library 1.1.1
40593  * Copyright(c) 2006-2007, Ext JS, LLC.
40594  *
40595  * Originally Released Under LGPL - original licence link has changed is not relivant.
40596  *
40597  * Fork - LGPL
40598  * <script type="text/javascript">
40599  */
40600  
40601
40602 /**
40603  * @class Roo.form.TextField
40604  * @extends Roo.form.Field
40605  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40606  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40607  * @constructor
40608  * Creates a new TextField
40609  * @param {Object} config Configuration options
40610  */
40611 Roo.form.TextField = function(config){
40612     Roo.form.TextField.superclass.constructor.call(this, config);
40613     this.addEvents({
40614         /**
40615          * @event autosize
40616          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40617          * according to the default logic, but this event provides a hook for the developer to apply additional
40618          * logic at runtime to resize the field if needed.
40619              * @param {Roo.form.Field} this This text field
40620              * @param {Number} width The new field width
40621              */
40622         autosize : true
40623     });
40624 };
40625
40626 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40627     /**
40628      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40629      */
40630     grow : false,
40631     /**
40632      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40633      */
40634     growMin : 30,
40635     /**
40636      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40637      */
40638     growMax : 800,
40639     /**
40640      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40641      */
40642     vtype : null,
40643     /**
40644      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40645      */
40646     maskRe : null,
40647     /**
40648      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40649      */
40650     disableKeyFilter : false,
40651     /**
40652      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40653      */
40654     allowBlank : true,
40655     /**
40656      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40657      */
40658     minLength : 0,
40659     /**
40660      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40661      */
40662     maxLength : Number.MAX_VALUE,
40663     /**
40664      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40665      */
40666     minLengthText : "The minimum length for this field is {0}",
40667     /**
40668      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40669      */
40670     maxLengthText : "The maximum length for this field is {0}",
40671     /**
40672      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40673      */
40674     selectOnFocus : false,
40675     /**
40676      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40677      */    
40678     allowLeadingSpace : false,
40679     /**
40680      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40681      */
40682     blankText : "This field is required",
40683     /**
40684      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40685      * If available, this function will be called only after the basic validators all return true, and will be passed the
40686      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40687      */
40688     validator : null,
40689     /**
40690      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40691      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40692      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40693      */
40694     regex : null,
40695     /**
40696      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40697      */
40698     regexText : "",
40699     /**
40700      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40701      */
40702     emptyText : null,
40703    
40704
40705     // private
40706     initEvents : function()
40707     {
40708         if (this.emptyText) {
40709             this.el.attr('placeholder', this.emptyText);
40710         }
40711         
40712         Roo.form.TextField.superclass.initEvents.call(this);
40713         if(this.validationEvent == 'keyup'){
40714             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40715             this.el.on('keyup', this.filterValidation, this);
40716         }
40717         else if(this.validationEvent !== false){
40718             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40719         }
40720         
40721         if(this.selectOnFocus){
40722             this.on("focus", this.preFocus, this);
40723         }
40724         if (!this.allowLeadingSpace) {
40725             this.on('blur', this.cleanLeadingSpace, this);
40726         }
40727         
40728         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40729             this.el.on("keypress", this.filterKeys, this);
40730         }
40731         if(this.grow){
40732             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40733             this.el.on("click", this.autoSize,  this);
40734         }
40735         if(this.el.is('input[type=password]') && Roo.isSafari){
40736             this.el.on('keydown', this.SafariOnKeyDown, this);
40737         }
40738     },
40739
40740     processValue : function(value){
40741         if(this.stripCharsRe){
40742             var newValue = value.replace(this.stripCharsRe, '');
40743             if(newValue !== value){
40744                 this.setRawValue(newValue);
40745                 return newValue;
40746             }
40747         }
40748         return value;
40749     },
40750
40751     filterValidation : function(e){
40752         if(!e.isNavKeyPress()){
40753             this.validationTask.delay(this.validationDelay);
40754         }
40755     },
40756
40757     // private
40758     onKeyUp : function(e){
40759         if(!e.isNavKeyPress()){
40760             this.autoSize();
40761         }
40762     },
40763     // private - clean the leading white space
40764     cleanLeadingSpace : function(e)
40765     {
40766         if ( this.inputType == 'file') {
40767             return;
40768         }
40769         
40770         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40771     },
40772     /**
40773      * Resets the current field value to the originally-loaded value and clears any validation messages.
40774      *  
40775      */
40776     reset : function(){
40777         Roo.form.TextField.superclass.reset.call(this);
40778        
40779     }, 
40780     // private
40781     preFocus : function(){
40782         
40783         if(this.selectOnFocus){
40784             this.el.dom.select();
40785         }
40786     },
40787
40788     
40789     // private
40790     filterKeys : function(e){
40791         var k = e.getKey();
40792         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40793             return;
40794         }
40795         var c = e.getCharCode(), cc = String.fromCharCode(c);
40796         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40797             return;
40798         }
40799         if(!this.maskRe.test(cc)){
40800             e.stopEvent();
40801         }
40802     },
40803
40804     setValue : function(v){
40805         
40806         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40807         
40808         this.autoSize();
40809     },
40810
40811     /**
40812      * Validates a value according to the field's validation rules and marks the field as invalid
40813      * if the validation fails
40814      * @param {Mixed} value The value to validate
40815      * @return {Boolean} True if the value is valid, else false
40816      */
40817     validateValue : function(value){
40818         if(value.length < 1)  { // if it's blank
40819              if(this.allowBlank){
40820                 this.clearInvalid();
40821                 return true;
40822              }else{
40823                 this.markInvalid(this.blankText);
40824                 return false;
40825              }
40826         }
40827         if(value.length < this.minLength){
40828             this.markInvalid(String.format(this.minLengthText, this.minLength));
40829             return false;
40830         }
40831         if(value.length > this.maxLength){
40832             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40833             return false;
40834         }
40835         if(this.vtype){
40836             var vt = Roo.form.VTypes;
40837             if(!vt[this.vtype](value, this)){
40838                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40839                 return false;
40840             }
40841         }
40842         if(typeof this.validator == "function"){
40843             var msg = this.validator(value);
40844             if(msg !== true){
40845                 this.markInvalid(msg);
40846                 return false;
40847             }
40848         }
40849         if(this.regex && !this.regex.test(value)){
40850             this.markInvalid(this.regexText);
40851             return false;
40852         }
40853         return true;
40854     },
40855
40856     /**
40857      * Selects text in this field
40858      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40859      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40860      */
40861     selectText : function(start, end){
40862         var v = this.getRawValue();
40863         if(v.length > 0){
40864             start = start === undefined ? 0 : start;
40865             end = end === undefined ? v.length : end;
40866             var d = this.el.dom;
40867             if(d.setSelectionRange){
40868                 d.setSelectionRange(start, end);
40869             }else if(d.createTextRange){
40870                 var range = d.createTextRange();
40871                 range.moveStart("character", start);
40872                 range.moveEnd("character", v.length-end);
40873                 range.select();
40874             }
40875         }
40876     },
40877
40878     /**
40879      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40880      * This only takes effect if grow = true, and fires the autosize event.
40881      */
40882     autoSize : function(){
40883         if(!this.grow || !this.rendered){
40884             return;
40885         }
40886         if(!this.metrics){
40887             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40888         }
40889         var el = this.el;
40890         var v = el.dom.value;
40891         var d = document.createElement('div');
40892         d.appendChild(document.createTextNode(v));
40893         v = d.innerHTML;
40894         d = null;
40895         v += "&#160;";
40896         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40897         this.el.setWidth(w);
40898         this.fireEvent("autosize", this, w);
40899     },
40900     
40901     // private
40902     SafariOnKeyDown : function(event)
40903     {
40904         // this is a workaround for a password hang bug on chrome/ webkit.
40905         
40906         var isSelectAll = false;
40907         
40908         if(this.el.dom.selectionEnd > 0){
40909             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40910         }
40911         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40912             event.preventDefault();
40913             this.setValue('');
40914             return;
40915         }
40916         
40917         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40918             
40919             event.preventDefault();
40920             // this is very hacky as keydown always get's upper case.
40921             
40922             var cc = String.fromCharCode(event.getCharCode());
40923             
40924             
40925             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40926             
40927         }
40928         
40929         
40930     }
40931 });/*
40932  * Based on:
40933  * Ext JS Library 1.1.1
40934  * Copyright(c) 2006-2007, Ext JS, LLC.
40935  *
40936  * Originally Released Under LGPL - original licence link has changed is not relivant.
40937  *
40938  * Fork - LGPL
40939  * <script type="text/javascript">
40940  */
40941  
40942 /**
40943  * @class Roo.form.Hidden
40944  * @extends Roo.form.TextField
40945  * Simple Hidden element used on forms 
40946  * 
40947  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40948  * 
40949  * @constructor
40950  * Creates a new Hidden form element.
40951  * @param {Object} config Configuration options
40952  */
40953
40954
40955
40956 // easy hidden field...
40957 Roo.form.Hidden = function(config){
40958     Roo.form.Hidden.superclass.constructor.call(this, config);
40959 };
40960   
40961 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40962     fieldLabel:      '',
40963     inputType:      'hidden',
40964     width:          50,
40965     allowBlank:     true,
40966     labelSeparator: '',
40967     hidden:         true,
40968     itemCls :       'x-form-item-display-none'
40969
40970
40971 });
40972
40973
40974 /*
40975  * Based on:
40976  * Ext JS Library 1.1.1
40977  * Copyright(c) 2006-2007, Ext JS, LLC.
40978  *
40979  * Originally Released Under LGPL - original licence link has changed is not relivant.
40980  *
40981  * Fork - LGPL
40982  * <script type="text/javascript">
40983  */
40984  
40985 /**
40986  * @class Roo.form.TriggerField
40987  * @extends Roo.form.TextField
40988  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40989  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40990  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40991  * for which you can provide a custom implementation.  For example:
40992  * <pre><code>
40993 var trigger = new Roo.form.TriggerField();
40994 trigger.onTriggerClick = myTriggerFn;
40995 trigger.applyTo('my-field');
40996 </code></pre>
40997  *
40998  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40999  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41000  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41001  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41002  * @constructor
41003  * Create a new TriggerField.
41004  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41005  * to the base TextField)
41006  */
41007 Roo.form.TriggerField = function(config){
41008     this.mimicing = false;
41009     Roo.form.TriggerField.superclass.constructor.call(this, config);
41010 };
41011
41012 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41013     /**
41014      * @cfg {String} triggerClass A CSS class to apply to the trigger
41015      */
41016     /**
41017      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41018      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41019      */
41020     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41021     /**
41022      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41023      */
41024     hideTrigger:false,
41025
41026     /** @cfg {Boolean} grow @hide */
41027     /** @cfg {Number} growMin @hide */
41028     /** @cfg {Number} growMax @hide */
41029
41030     /**
41031      * @hide 
41032      * @method
41033      */
41034     autoSize: Roo.emptyFn,
41035     // private
41036     monitorTab : true,
41037     // private
41038     deferHeight : true,
41039
41040     
41041     actionMode : 'wrap',
41042     // private
41043     onResize : function(w, h){
41044         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41045         if(typeof w == 'number'){
41046             var x = w - this.trigger.getWidth();
41047             this.el.setWidth(this.adjustWidth('input', x));
41048             this.trigger.setStyle('left', x+'px');
41049         }
41050     },
41051
41052     // private
41053     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41054
41055     // private
41056     getResizeEl : function(){
41057         return this.wrap;
41058     },
41059
41060     // private
41061     getPositionEl : function(){
41062         return this.wrap;
41063     },
41064
41065     // private
41066     alignErrorIcon : function(){
41067         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41068     },
41069
41070     // private
41071     onRender : function(ct, position){
41072         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41073         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41074         this.trigger = this.wrap.createChild(this.triggerConfig ||
41075                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41076         if(this.hideTrigger){
41077             this.trigger.setDisplayed(false);
41078         }
41079         this.initTrigger();
41080         if(!this.width){
41081             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41082         }
41083     },
41084
41085     // private
41086     initTrigger : function(){
41087         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41088         this.trigger.addClassOnOver('x-form-trigger-over');
41089         this.trigger.addClassOnClick('x-form-trigger-click');
41090     },
41091
41092     // private
41093     onDestroy : function(){
41094         if(this.trigger){
41095             this.trigger.removeAllListeners();
41096             this.trigger.remove();
41097         }
41098         if(this.wrap){
41099             this.wrap.remove();
41100         }
41101         Roo.form.TriggerField.superclass.onDestroy.call(this);
41102     },
41103
41104     // private
41105     onFocus : function(){
41106         Roo.form.TriggerField.superclass.onFocus.call(this);
41107         if(!this.mimicing){
41108             this.wrap.addClass('x-trigger-wrap-focus');
41109             this.mimicing = true;
41110             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41111             if(this.monitorTab){
41112                 this.el.on("keydown", this.checkTab, this);
41113             }
41114         }
41115     },
41116
41117     // private
41118     checkTab : function(e){
41119         if(e.getKey() == e.TAB){
41120             this.triggerBlur();
41121         }
41122     },
41123
41124     // private
41125     onBlur : function(){
41126         // do nothing
41127     },
41128
41129     // private
41130     mimicBlur : function(e, t){
41131         if(!this.wrap.contains(t) && this.validateBlur()){
41132             this.triggerBlur();
41133         }
41134     },
41135
41136     // private
41137     triggerBlur : function(){
41138         this.mimicing = false;
41139         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41140         if(this.monitorTab){
41141             this.el.un("keydown", this.checkTab, this);
41142         }
41143         this.wrap.removeClass('x-trigger-wrap-focus');
41144         Roo.form.TriggerField.superclass.onBlur.call(this);
41145     },
41146
41147     // private
41148     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41149     validateBlur : function(e, t){
41150         return true;
41151     },
41152
41153     // private
41154     onDisable : function(){
41155         Roo.form.TriggerField.superclass.onDisable.call(this);
41156         if(this.wrap){
41157             this.wrap.addClass('x-item-disabled');
41158         }
41159     },
41160
41161     // private
41162     onEnable : function(){
41163         Roo.form.TriggerField.superclass.onEnable.call(this);
41164         if(this.wrap){
41165             this.wrap.removeClass('x-item-disabled');
41166         }
41167     },
41168
41169     // private
41170     onShow : function(){
41171         var ae = this.getActionEl();
41172         
41173         if(ae){
41174             ae.dom.style.display = '';
41175             ae.dom.style.visibility = 'visible';
41176         }
41177     },
41178
41179     // private
41180     
41181     onHide : function(){
41182         var ae = this.getActionEl();
41183         ae.dom.style.display = 'none';
41184     },
41185
41186     /**
41187      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41188      * by an implementing function.
41189      * @method
41190      * @param {EventObject} e
41191      */
41192     onTriggerClick : Roo.emptyFn
41193 });
41194
41195 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41196 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41197 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41198 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41199     initComponent : function(){
41200         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41201
41202         this.triggerConfig = {
41203             tag:'span', cls:'x-form-twin-triggers', cn:[
41204             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41205             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41206         ]};
41207     },
41208
41209     getTrigger : function(index){
41210         return this.triggers[index];
41211     },
41212
41213     initTrigger : function(){
41214         var ts = this.trigger.select('.x-form-trigger', true);
41215         this.wrap.setStyle('overflow', 'hidden');
41216         var triggerField = this;
41217         ts.each(function(t, all, index){
41218             t.hide = function(){
41219                 var w = triggerField.wrap.getWidth();
41220                 this.dom.style.display = 'none';
41221                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41222             };
41223             t.show = function(){
41224                 var w = triggerField.wrap.getWidth();
41225                 this.dom.style.display = '';
41226                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41227             };
41228             var triggerIndex = 'Trigger'+(index+1);
41229
41230             if(this['hide'+triggerIndex]){
41231                 t.dom.style.display = 'none';
41232             }
41233             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41234             t.addClassOnOver('x-form-trigger-over');
41235             t.addClassOnClick('x-form-trigger-click');
41236         }, this);
41237         this.triggers = ts.elements;
41238     },
41239
41240     onTrigger1Click : Roo.emptyFn,
41241     onTrigger2Click : Roo.emptyFn
41242 });/*
41243  * Based on:
41244  * Ext JS Library 1.1.1
41245  * Copyright(c) 2006-2007, Ext JS, LLC.
41246  *
41247  * Originally Released Under LGPL - original licence link has changed is not relivant.
41248  *
41249  * Fork - LGPL
41250  * <script type="text/javascript">
41251  */
41252  
41253 /**
41254  * @class Roo.form.TextArea
41255  * @extends Roo.form.TextField
41256  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41257  * support for auto-sizing.
41258  * @constructor
41259  * Creates a new TextArea
41260  * @param {Object} config Configuration options
41261  */
41262 Roo.form.TextArea = function(config){
41263     Roo.form.TextArea.superclass.constructor.call(this, config);
41264     // these are provided exchanges for backwards compat
41265     // minHeight/maxHeight were replaced by growMin/growMax to be
41266     // compatible with TextField growing config values
41267     if(this.minHeight !== undefined){
41268         this.growMin = this.minHeight;
41269     }
41270     if(this.maxHeight !== undefined){
41271         this.growMax = this.maxHeight;
41272     }
41273 };
41274
41275 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41276     /**
41277      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41278      */
41279     growMin : 60,
41280     /**
41281      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41282      */
41283     growMax: 1000,
41284     /**
41285      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41286      * in the field (equivalent to setting overflow: hidden, defaults to false)
41287      */
41288     preventScrollbars: false,
41289     /**
41290      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41291      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41292      */
41293
41294     // private
41295     onRender : function(ct, position){
41296         if(!this.el){
41297             this.defaultAutoCreate = {
41298                 tag: "textarea",
41299                 style:"width:300px;height:60px;",
41300                 autocomplete: "new-password"
41301             };
41302         }
41303         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41304         if(this.grow){
41305             this.textSizeEl = Roo.DomHelper.append(document.body, {
41306                 tag: "pre", cls: "x-form-grow-sizer"
41307             });
41308             if(this.preventScrollbars){
41309                 this.el.setStyle("overflow", "hidden");
41310             }
41311             this.el.setHeight(this.growMin);
41312         }
41313     },
41314
41315     onDestroy : function(){
41316         if(this.textSizeEl){
41317             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41318         }
41319         Roo.form.TextArea.superclass.onDestroy.call(this);
41320     },
41321
41322     // private
41323     onKeyUp : function(e){
41324         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41325             this.autoSize();
41326         }
41327     },
41328
41329     /**
41330      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41331      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41332      */
41333     autoSize : function(){
41334         if(!this.grow || !this.textSizeEl){
41335             return;
41336         }
41337         var el = this.el;
41338         var v = el.dom.value;
41339         var ts = this.textSizeEl;
41340
41341         ts.innerHTML = '';
41342         ts.appendChild(document.createTextNode(v));
41343         v = ts.innerHTML;
41344
41345         Roo.fly(ts).setWidth(this.el.getWidth());
41346         if(v.length < 1){
41347             v = "&#160;&#160;";
41348         }else{
41349             if(Roo.isIE){
41350                 v = v.replace(/\n/g, '<p>&#160;</p>');
41351             }
41352             v += "&#160;\n&#160;";
41353         }
41354         ts.innerHTML = v;
41355         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41356         if(h != this.lastHeight){
41357             this.lastHeight = h;
41358             this.el.setHeight(h);
41359             this.fireEvent("autosize", this, h);
41360         }
41361     }
41362 });/*
41363  * Based on:
41364  * Ext JS Library 1.1.1
41365  * Copyright(c) 2006-2007, Ext JS, LLC.
41366  *
41367  * Originally Released Under LGPL - original licence link has changed is not relivant.
41368  *
41369  * Fork - LGPL
41370  * <script type="text/javascript">
41371  */
41372  
41373
41374 /**
41375  * @class Roo.form.NumberField
41376  * @extends Roo.form.TextField
41377  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41378  * @constructor
41379  * Creates a new NumberField
41380  * @param {Object} config Configuration options
41381  */
41382 Roo.form.NumberField = function(config){
41383     Roo.form.NumberField.superclass.constructor.call(this, config);
41384 };
41385
41386 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41387     /**
41388      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41389      */
41390     fieldClass: "x-form-field x-form-num-field",
41391     /**
41392      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41393      */
41394     allowDecimals : true,
41395     /**
41396      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41397      */
41398     decimalSeparator : ".",
41399     /**
41400      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41401      */
41402     decimalPrecision : 2,
41403     /**
41404      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41405      */
41406     allowNegative : true,
41407     /**
41408      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41409      */
41410     minValue : Number.NEGATIVE_INFINITY,
41411     /**
41412      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41413      */
41414     maxValue : Number.MAX_VALUE,
41415     /**
41416      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41417      */
41418     minText : "The minimum value for this field is {0}",
41419     /**
41420      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41421      */
41422     maxText : "The maximum value for this field is {0}",
41423     /**
41424      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41425      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41426      */
41427     nanText : "{0} is not a valid number",
41428
41429     // private
41430     initEvents : function(){
41431         Roo.form.NumberField.superclass.initEvents.call(this);
41432         var allowed = "0123456789";
41433         if(this.allowDecimals){
41434             allowed += this.decimalSeparator;
41435         }
41436         if(this.allowNegative){
41437             allowed += "-";
41438         }
41439         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41440         var keyPress = function(e){
41441             var k = e.getKey();
41442             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41443                 return;
41444             }
41445             var c = e.getCharCode();
41446             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41447                 e.stopEvent();
41448             }
41449         };
41450         this.el.on("keypress", keyPress, this);
41451     },
41452
41453     // private
41454     validateValue : function(value){
41455         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41456             return false;
41457         }
41458         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41459              return true;
41460         }
41461         var num = this.parseValue(value);
41462         if(isNaN(num)){
41463             this.markInvalid(String.format(this.nanText, value));
41464             return false;
41465         }
41466         if(num < this.minValue){
41467             this.markInvalid(String.format(this.minText, this.minValue));
41468             return false;
41469         }
41470         if(num > this.maxValue){
41471             this.markInvalid(String.format(this.maxText, this.maxValue));
41472             return false;
41473         }
41474         return true;
41475     },
41476
41477     getValue : function(){
41478         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41479     },
41480
41481     // private
41482     parseValue : function(value){
41483         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41484         return isNaN(value) ? '' : value;
41485     },
41486
41487     // private
41488     fixPrecision : function(value){
41489         var nan = isNaN(value);
41490         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41491             return nan ? '' : value;
41492         }
41493         return parseFloat(value).toFixed(this.decimalPrecision);
41494     },
41495
41496     setValue : function(v){
41497         v = this.fixPrecision(v);
41498         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41499     },
41500
41501     // private
41502     decimalPrecisionFcn : function(v){
41503         return Math.floor(v);
41504     },
41505
41506     beforeBlur : function(){
41507         var v = this.parseValue(this.getRawValue());
41508         if(v){
41509             this.setValue(v);
41510         }
41511     }
41512 });/*
41513  * Based on:
41514  * Ext JS Library 1.1.1
41515  * Copyright(c) 2006-2007, Ext JS, LLC.
41516  *
41517  * Originally Released Under LGPL - original licence link has changed is not relivant.
41518  *
41519  * Fork - LGPL
41520  * <script type="text/javascript">
41521  */
41522  
41523 /**
41524  * @class Roo.form.DateField
41525  * @extends Roo.form.TriggerField
41526  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41527 * @constructor
41528 * Create a new DateField
41529 * @param {Object} config
41530  */
41531 Roo.form.DateField = function(config)
41532 {
41533     Roo.form.DateField.superclass.constructor.call(this, config);
41534     
41535       this.addEvents({
41536          
41537         /**
41538          * @event select
41539          * Fires when a date is selected
41540              * @param {Roo.form.DateField} combo This combo box
41541              * @param {Date} date The date selected
41542              */
41543         'select' : true
41544          
41545     });
41546     
41547     
41548     if(typeof this.minValue == "string") {
41549         this.minValue = this.parseDate(this.minValue);
41550     }
41551     if(typeof this.maxValue == "string") {
41552         this.maxValue = this.parseDate(this.maxValue);
41553     }
41554     this.ddMatch = null;
41555     if(this.disabledDates){
41556         var dd = this.disabledDates;
41557         var re = "(?:";
41558         for(var i = 0; i < dd.length; i++){
41559             re += dd[i];
41560             if(i != dd.length-1) {
41561                 re += "|";
41562             }
41563         }
41564         this.ddMatch = new RegExp(re + ")");
41565     }
41566 };
41567
41568 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41569     /**
41570      * @cfg {String} format
41571      * The default date format string which can be overriden for localization support.  The format must be
41572      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41573      */
41574     format : "m/d/y",
41575     /**
41576      * @cfg {String} altFormats
41577      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41578      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41579      */
41580     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41581     /**
41582      * @cfg {Array} disabledDays
41583      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41584      */
41585     disabledDays : null,
41586     /**
41587      * @cfg {String} disabledDaysText
41588      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41589      */
41590     disabledDaysText : "Disabled",
41591     /**
41592      * @cfg {Array} disabledDates
41593      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41594      * expression so they are very powerful. Some examples:
41595      * <ul>
41596      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41597      * <li>["03/08", "09/16"] would disable those days for every year</li>
41598      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41599      * <li>["03/../2006"] would disable every day in March 2006</li>
41600      * <li>["^03"] would disable every day in every March</li>
41601      * </ul>
41602      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41603      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41604      */
41605     disabledDates : null,
41606     /**
41607      * @cfg {String} disabledDatesText
41608      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41609      */
41610     disabledDatesText : "Disabled",
41611         
41612         
41613         /**
41614      * @cfg {Date/String} zeroValue
41615      * if the date is less that this number, then the field is rendered as empty
41616      * default is 1800
41617      */
41618         zeroValue : '1800-01-01',
41619         
41620         
41621     /**
41622      * @cfg {Date/String} minValue
41623      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41624      * valid format (defaults to null).
41625      */
41626     minValue : null,
41627     /**
41628      * @cfg {Date/String} maxValue
41629      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41630      * valid format (defaults to null).
41631      */
41632     maxValue : null,
41633     /**
41634      * @cfg {String} minText
41635      * The error text to display when the date in the cell is before minValue (defaults to
41636      * 'The date in this field must be after {minValue}').
41637      */
41638     minText : "The date in this field must be equal to or after {0}",
41639     /**
41640      * @cfg {String} maxText
41641      * The error text to display when the date in the cell is after maxValue (defaults to
41642      * 'The date in this field must be before {maxValue}').
41643      */
41644     maxText : "The date in this field must be equal to or before {0}",
41645     /**
41646      * @cfg {String} invalidText
41647      * The error text to display when the date in the field is invalid (defaults to
41648      * '{value} is not a valid date - it must be in the format {format}').
41649      */
41650     invalidText : "{0} is not a valid date - it must be in the format {1}",
41651     /**
41652      * @cfg {String} triggerClass
41653      * An additional CSS class used to style the trigger button.  The trigger will always get the
41654      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41655      * which displays a calendar icon).
41656      */
41657     triggerClass : 'x-form-date-trigger',
41658     
41659
41660     /**
41661      * @cfg {Boolean} useIso
41662      * if enabled, then the date field will use a hidden field to store the 
41663      * real value as iso formated date. default (false)
41664      */ 
41665     useIso : false,
41666     /**
41667      * @cfg {String/Object} autoCreate
41668      * A DomHelper element spec, or true for a default element spec (defaults to
41669      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41670      */ 
41671     // private
41672     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41673     
41674     // private
41675     hiddenField: false,
41676     
41677     onRender : function(ct, position)
41678     {
41679         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41680         if (this.useIso) {
41681             //this.el.dom.removeAttribute('name'); 
41682             Roo.log("Changing name?");
41683             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41684             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41685                     'before', true);
41686             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41687             // prevent input submission
41688             this.hiddenName = this.name;
41689         }
41690             
41691             
41692     },
41693     
41694     // private
41695     validateValue : function(value)
41696     {
41697         value = this.formatDate(value);
41698         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41699             Roo.log('super failed');
41700             return false;
41701         }
41702         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41703              return true;
41704         }
41705         var svalue = value;
41706         value = this.parseDate(value);
41707         if(!value){
41708             Roo.log('parse date failed' + svalue);
41709             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41710             return false;
41711         }
41712         var time = value.getTime();
41713         if(this.minValue && time < this.minValue.getTime()){
41714             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41715             return false;
41716         }
41717         if(this.maxValue && time > this.maxValue.getTime()){
41718             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41719             return false;
41720         }
41721         if(this.disabledDays){
41722             var day = value.getDay();
41723             for(var i = 0; i < this.disabledDays.length; i++) {
41724                 if(day === this.disabledDays[i]){
41725                     this.markInvalid(this.disabledDaysText);
41726                     return false;
41727                 }
41728             }
41729         }
41730         var fvalue = this.formatDate(value);
41731         if(this.ddMatch && this.ddMatch.test(fvalue)){
41732             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41733             return false;
41734         }
41735         return true;
41736     },
41737
41738     // private
41739     // Provides logic to override the default TriggerField.validateBlur which just returns true
41740     validateBlur : function(){
41741         return !this.menu || !this.menu.isVisible();
41742     },
41743     
41744     getName: function()
41745     {
41746         // returns hidden if it's set..
41747         if (!this.rendered) {return ''};
41748         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41749         
41750     },
41751
41752     /**
41753      * Returns the current date value of the date field.
41754      * @return {Date} The date value
41755      */
41756     getValue : function(){
41757         
41758         return  this.hiddenField ?
41759                 this.hiddenField.value :
41760                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41761     },
41762
41763     /**
41764      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41765      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41766      * (the default format used is "m/d/y").
41767      * <br />Usage:
41768      * <pre><code>
41769 //All of these calls set the same date value (May 4, 2006)
41770
41771 //Pass a date object:
41772 var dt = new Date('5/4/06');
41773 dateField.setValue(dt);
41774
41775 //Pass a date string (default format):
41776 dateField.setValue('5/4/06');
41777
41778 //Pass a date string (custom format):
41779 dateField.format = 'Y-m-d';
41780 dateField.setValue('2006-5-4');
41781 </code></pre>
41782      * @param {String/Date} date The date or valid date string
41783      */
41784     setValue : function(date){
41785         if (this.hiddenField) {
41786             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41787         }
41788         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41789         // make sure the value field is always stored as a date..
41790         this.value = this.parseDate(date);
41791         
41792         
41793     },
41794
41795     // private
41796     parseDate : function(value){
41797                 
41798                 if (value instanceof Date) {
41799                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41800                                 return  '';
41801                         }
41802                         return value;
41803                 }
41804                 
41805                 
41806         if(!value || value instanceof Date){
41807             return value;
41808         }
41809         var v = Date.parseDate(value, this.format);
41810          if (!v && this.useIso) {
41811             v = Date.parseDate(value, 'Y-m-d');
41812         }
41813         if(!v && this.altFormats){
41814             if(!this.altFormatsArray){
41815                 this.altFormatsArray = this.altFormats.split("|");
41816             }
41817             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41818                 v = Date.parseDate(value, this.altFormatsArray[i]);
41819             }
41820         }
41821                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
41822                         v = '';
41823                 }
41824         return v;
41825     },
41826
41827     // private
41828     formatDate : function(date, fmt){
41829         return (!date || !(date instanceof Date)) ?
41830                date : date.dateFormat(fmt || this.format);
41831     },
41832
41833     // private
41834     menuListeners : {
41835         select: function(m, d){
41836             
41837             this.setValue(d);
41838             this.fireEvent('select', this, d);
41839         },
41840         show : function(){ // retain focus styling
41841             this.onFocus();
41842         },
41843         hide : function(){
41844             this.focus.defer(10, this);
41845             var ml = this.menuListeners;
41846             this.menu.un("select", ml.select,  this);
41847             this.menu.un("show", ml.show,  this);
41848             this.menu.un("hide", ml.hide,  this);
41849         }
41850     },
41851
41852     // private
41853     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41854     onTriggerClick : function(){
41855         if(this.disabled){
41856             return;
41857         }
41858         if(this.menu == null){
41859             this.menu = new Roo.menu.DateMenu();
41860         }
41861         Roo.apply(this.menu.picker,  {
41862             showClear: this.allowBlank,
41863             minDate : this.minValue,
41864             maxDate : this.maxValue,
41865             disabledDatesRE : this.ddMatch,
41866             disabledDatesText : this.disabledDatesText,
41867             disabledDays : this.disabledDays,
41868             disabledDaysText : this.disabledDaysText,
41869             format : this.useIso ? 'Y-m-d' : this.format,
41870             minText : String.format(this.minText, this.formatDate(this.minValue)),
41871             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41872         });
41873         this.menu.on(Roo.apply({}, this.menuListeners, {
41874             scope:this
41875         }));
41876         this.menu.picker.setValue(this.getValue() || new Date());
41877         this.menu.show(this.el, "tl-bl?");
41878     },
41879
41880     beforeBlur : function(){
41881         var v = this.parseDate(this.getRawValue());
41882         if(v){
41883             this.setValue(v);
41884         }
41885     },
41886
41887     /*@
41888      * overide
41889      * 
41890      */
41891     isDirty : function() {
41892         if(this.disabled) {
41893             return false;
41894         }
41895         
41896         if(typeof(this.startValue) === 'undefined'){
41897             return false;
41898         }
41899         
41900         return String(this.getValue()) !== String(this.startValue);
41901         
41902     },
41903     // @overide
41904     cleanLeadingSpace : function(e)
41905     {
41906        return;
41907     }
41908     
41909 });/*
41910  * Based on:
41911  * Ext JS Library 1.1.1
41912  * Copyright(c) 2006-2007, Ext JS, LLC.
41913  *
41914  * Originally Released Under LGPL - original licence link has changed is not relivant.
41915  *
41916  * Fork - LGPL
41917  * <script type="text/javascript">
41918  */
41919  
41920 /**
41921  * @class Roo.form.MonthField
41922  * @extends Roo.form.TriggerField
41923  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41924 * @constructor
41925 * Create a new MonthField
41926 * @param {Object} config
41927  */
41928 Roo.form.MonthField = function(config){
41929     
41930     Roo.form.MonthField.superclass.constructor.call(this, config);
41931     
41932       this.addEvents({
41933          
41934         /**
41935          * @event select
41936          * Fires when a date is selected
41937              * @param {Roo.form.MonthFieeld} combo This combo box
41938              * @param {Date} date The date selected
41939              */
41940         'select' : true
41941          
41942     });
41943     
41944     
41945     if(typeof this.minValue == "string") {
41946         this.minValue = this.parseDate(this.minValue);
41947     }
41948     if(typeof this.maxValue == "string") {
41949         this.maxValue = this.parseDate(this.maxValue);
41950     }
41951     this.ddMatch = null;
41952     if(this.disabledDates){
41953         var dd = this.disabledDates;
41954         var re = "(?:";
41955         for(var i = 0; i < dd.length; i++){
41956             re += dd[i];
41957             if(i != dd.length-1) {
41958                 re += "|";
41959             }
41960         }
41961         this.ddMatch = new RegExp(re + ")");
41962     }
41963 };
41964
41965 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41966     /**
41967      * @cfg {String} format
41968      * The default date format string which can be overriden for localization support.  The format must be
41969      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41970      */
41971     format : "M Y",
41972     /**
41973      * @cfg {String} altFormats
41974      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41975      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41976      */
41977     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41978     /**
41979      * @cfg {Array} disabledDays
41980      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41981      */
41982     disabledDays : [0,1,2,3,4,5,6],
41983     /**
41984      * @cfg {String} disabledDaysText
41985      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41986      */
41987     disabledDaysText : "Disabled",
41988     /**
41989      * @cfg {Array} disabledDates
41990      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41991      * expression so they are very powerful. Some examples:
41992      * <ul>
41993      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41994      * <li>["03/08", "09/16"] would disable those days for every year</li>
41995      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41996      * <li>["03/../2006"] would disable every day in March 2006</li>
41997      * <li>["^03"] would disable every day in every March</li>
41998      * </ul>
41999      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42000      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42001      */
42002     disabledDates : null,
42003     /**
42004      * @cfg {String} disabledDatesText
42005      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42006      */
42007     disabledDatesText : "Disabled",
42008     /**
42009      * @cfg {Date/String} minValue
42010      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42011      * valid format (defaults to null).
42012      */
42013     minValue : null,
42014     /**
42015      * @cfg {Date/String} maxValue
42016      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42017      * valid format (defaults to null).
42018      */
42019     maxValue : null,
42020     /**
42021      * @cfg {String} minText
42022      * The error text to display when the date in the cell is before minValue (defaults to
42023      * 'The date in this field must be after {minValue}').
42024      */
42025     minText : "The date in this field must be equal to or after {0}",
42026     /**
42027      * @cfg {String} maxTextf
42028      * The error text to display when the date in the cell is after maxValue (defaults to
42029      * 'The date in this field must be before {maxValue}').
42030      */
42031     maxText : "The date in this field must be equal to or before {0}",
42032     /**
42033      * @cfg {String} invalidText
42034      * The error text to display when the date in the field is invalid (defaults to
42035      * '{value} is not a valid date - it must be in the format {format}').
42036      */
42037     invalidText : "{0} is not a valid date - it must be in the format {1}",
42038     /**
42039      * @cfg {String} triggerClass
42040      * An additional CSS class used to style the trigger button.  The trigger will always get the
42041      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42042      * which displays a calendar icon).
42043      */
42044     triggerClass : 'x-form-date-trigger',
42045     
42046
42047     /**
42048      * @cfg {Boolean} useIso
42049      * if enabled, then the date field will use a hidden field to store the 
42050      * real value as iso formated date. default (true)
42051      */ 
42052     useIso : true,
42053     /**
42054      * @cfg {String/Object} autoCreate
42055      * A DomHelper element spec, or true for a default element spec (defaults to
42056      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42057      */ 
42058     // private
42059     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42060     
42061     // private
42062     hiddenField: false,
42063     
42064     hideMonthPicker : false,
42065     
42066     onRender : function(ct, position)
42067     {
42068         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42069         if (this.useIso) {
42070             this.el.dom.removeAttribute('name'); 
42071             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42072                     'before', true);
42073             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42074             // prevent input submission
42075             this.hiddenName = this.name;
42076         }
42077             
42078             
42079     },
42080     
42081     // private
42082     validateValue : function(value)
42083     {
42084         value = this.formatDate(value);
42085         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42086             return false;
42087         }
42088         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42089              return true;
42090         }
42091         var svalue = value;
42092         value = this.parseDate(value);
42093         if(!value){
42094             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42095             return false;
42096         }
42097         var time = value.getTime();
42098         if(this.minValue && time < this.minValue.getTime()){
42099             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42100             return false;
42101         }
42102         if(this.maxValue && time > this.maxValue.getTime()){
42103             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42104             return false;
42105         }
42106         /*if(this.disabledDays){
42107             var day = value.getDay();
42108             for(var i = 0; i < this.disabledDays.length; i++) {
42109                 if(day === this.disabledDays[i]){
42110                     this.markInvalid(this.disabledDaysText);
42111                     return false;
42112                 }
42113             }
42114         }
42115         */
42116         var fvalue = this.formatDate(value);
42117         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42118             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42119             return false;
42120         }
42121         */
42122         return true;
42123     },
42124
42125     // private
42126     // Provides logic to override the default TriggerField.validateBlur which just returns true
42127     validateBlur : function(){
42128         return !this.menu || !this.menu.isVisible();
42129     },
42130
42131     /**
42132      * Returns the current date value of the date field.
42133      * @return {Date} The date value
42134      */
42135     getValue : function(){
42136         
42137         
42138         
42139         return  this.hiddenField ?
42140                 this.hiddenField.value :
42141                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42142     },
42143
42144     /**
42145      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42146      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42147      * (the default format used is "m/d/y").
42148      * <br />Usage:
42149      * <pre><code>
42150 //All of these calls set the same date value (May 4, 2006)
42151
42152 //Pass a date object:
42153 var dt = new Date('5/4/06');
42154 monthField.setValue(dt);
42155
42156 //Pass a date string (default format):
42157 monthField.setValue('5/4/06');
42158
42159 //Pass a date string (custom format):
42160 monthField.format = 'Y-m-d';
42161 monthField.setValue('2006-5-4');
42162 </code></pre>
42163      * @param {String/Date} date The date or valid date string
42164      */
42165     setValue : function(date){
42166         Roo.log('month setValue' + date);
42167         // can only be first of month..
42168         
42169         var val = this.parseDate(date);
42170         
42171         if (this.hiddenField) {
42172             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42173         }
42174         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42175         this.value = this.parseDate(date);
42176     },
42177
42178     // private
42179     parseDate : function(value){
42180         if(!value || value instanceof Date){
42181             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42182             return value;
42183         }
42184         var v = Date.parseDate(value, this.format);
42185         if (!v && this.useIso) {
42186             v = Date.parseDate(value, 'Y-m-d');
42187         }
42188         if (v) {
42189             // 
42190             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42191         }
42192         
42193         
42194         if(!v && this.altFormats){
42195             if(!this.altFormatsArray){
42196                 this.altFormatsArray = this.altFormats.split("|");
42197             }
42198             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42199                 v = Date.parseDate(value, this.altFormatsArray[i]);
42200             }
42201         }
42202         return v;
42203     },
42204
42205     // private
42206     formatDate : function(date, fmt){
42207         return (!date || !(date instanceof Date)) ?
42208                date : date.dateFormat(fmt || this.format);
42209     },
42210
42211     // private
42212     menuListeners : {
42213         select: function(m, d){
42214             this.setValue(d);
42215             this.fireEvent('select', this, d);
42216         },
42217         show : function(){ // retain focus styling
42218             this.onFocus();
42219         },
42220         hide : function(){
42221             this.focus.defer(10, this);
42222             var ml = this.menuListeners;
42223             this.menu.un("select", ml.select,  this);
42224             this.menu.un("show", ml.show,  this);
42225             this.menu.un("hide", ml.hide,  this);
42226         }
42227     },
42228     // private
42229     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42230     onTriggerClick : function(){
42231         if(this.disabled){
42232             return;
42233         }
42234         if(this.menu == null){
42235             this.menu = new Roo.menu.DateMenu();
42236            
42237         }
42238         
42239         Roo.apply(this.menu.picker,  {
42240             
42241             showClear: this.allowBlank,
42242             minDate : this.minValue,
42243             maxDate : this.maxValue,
42244             disabledDatesRE : this.ddMatch,
42245             disabledDatesText : this.disabledDatesText,
42246             
42247             format : this.useIso ? 'Y-m-d' : this.format,
42248             minText : String.format(this.minText, this.formatDate(this.minValue)),
42249             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42250             
42251         });
42252          this.menu.on(Roo.apply({}, this.menuListeners, {
42253             scope:this
42254         }));
42255        
42256         
42257         var m = this.menu;
42258         var p = m.picker;
42259         
42260         // hide month picker get's called when we called by 'before hide';
42261         
42262         var ignorehide = true;
42263         p.hideMonthPicker  = function(disableAnim){
42264             if (ignorehide) {
42265                 return;
42266             }
42267              if(this.monthPicker){
42268                 Roo.log("hideMonthPicker called");
42269                 if(disableAnim === true){
42270                     this.monthPicker.hide();
42271                 }else{
42272                     this.monthPicker.slideOut('t', {duration:.2});
42273                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42274                     p.fireEvent("select", this, this.value);
42275                     m.hide();
42276                 }
42277             }
42278         }
42279         
42280         Roo.log('picker set value');
42281         Roo.log(this.getValue());
42282         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42283         m.show(this.el, 'tl-bl?');
42284         ignorehide  = false;
42285         // this will trigger hideMonthPicker..
42286         
42287         
42288         // hidden the day picker
42289         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42290         
42291         
42292         
42293       
42294         
42295         p.showMonthPicker.defer(100, p);
42296     
42297         
42298        
42299     },
42300
42301     beforeBlur : function(){
42302         var v = this.parseDate(this.getRawValue());
42303         if(v){
42304             this.setValue(v);
42305         }
42306     }
42307
42308     /** @cfg {Boolean} grow @hide */
42309     /** @cfg {Number} growMin @hide */
42310     /** @cfg {Number} growMax @hide */
42311     /**
42312      * @hide
42313      * @method autoSize
42314      */
42315 });/*
42316  * Based on:
42317  * Ext JS Library 1.1.1
42318  * Copyright(c) 2006-2007, Ext JS, LLC.
42319  *
42320  * Originally Released Under LGPL - original licence link has changed is not relivant.
42321  *
42322  * Fork - LGPL
42323  * <script type="text/javascript">
42324  */
42325  
42326
42327 /**
42328  * @class Roo.form.ComboBox
42329  * @extends Roo.form.TriggerField
42330  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42331  * @constructor
42332  * Create a new ComboBox.
42333  * @param {Object} config Configuration options
42334  */
42335 Roo.form.ComboBox = function(config){
42336     Roo.form.ComboBox.superclass.constructor.call(this, config);
42337     this.addEvents({
42338         /**
42339          * @event expand
42340          * Fires when the dropdown list is expanded
42341              * @param {Roo.form.ComboBox} combo This combo box
42342              */
42343         'expand' : true,
42344         /**
42345          * @event collapse
42346          * Fires when the dropdown list is collapsed
42347              * @param {Roo.form.ComboBox} combo This combo box
42348              */
42349         'collapse' : true,
42350         /**
42351          * @event beforeselect
42352          * Fires before a list item is selected. Return false to cancel the selection.
42353              * @param {Roo.form.ComboBox} combo This combo box
42354              * @param {Roo.data.Record} record The data record returned from the underlying store
42355              * @param {Number} index The index of the selected item in the dropdown list
42356              */
42357         'beforeselect' : true,
42358         /**
42359          * @event select
42360          * Fires when a list item is selected
42361              * @param {Roo.form.ComboBox} combo This combo box
42362              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42363              * @param {Number} index The index of the selected item in the dropdown list
42364              */
42365         'select' : true,
42366         /**
42367          * @event beforequery
42368          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42369          * The event object passed has these properties:
42370              * @param {Roo.form.ComboBox} combo This combo box
42371              * @param {String} query The query
42372              * @param {Boolean} forceAll true to force "all" query
42373              * @param {Boolean} cancel true to cancel the query
42374              * @param {Object} e The query event object
42375              */
42376         'beforequery': true,
42377          /**
42378          * @event add
42379          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42380              * @param {Roo.form.ComboBox} combo This combo box
42381              */
42382         'add' : true,
42383         /**
42384          * @event edit
42385          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42386              * @param {Roo.form.ComboBox} combo This combo box
42387              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42388              */
42389         'edit' : true
42390         
42391         
42392     });
42393     if(this.transform){
42394         this.allowDomMove = false;
42395         var s = Roo.getDom(this.transform);
42396         if(!this.hiddenName){
42397             this.hiddenName = s.name;
42398         }
42399         if(!this.store){
42400             this.mode = 'local';
42401             var d = [], opts = s.options;
42402             for(var i = 0, len = opts.length;i < len; i++){
42403                 var o = opts[i];
42404                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42405                 if(o.selected) {
42406                     this.value = value;
42407                 }
42408                 d.push([value, o.text]);
42409             }
42410             this.store = new Roo.data.SimpleStore({
42411                 'id': 0,
42412                 fields: ['value', 'text'],
42413                 data : d
42414             });
42415             this.valueField = 'value';
42416             this.displayField = 'text';
42417         }
42418         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42419         if(!this.lazyRender){
42420             this.target = true;
42421             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42422             s.parentNode.removeChild(s); // remove it
42423             this.render(this.el.parentNode);
42424         }else{
42425             s.parentNode.removeChild(s); // remove it
42426         }
42427
42428     }
42429     if (this.store) {
42430         this.store = Roo.factory(this.store, Roo.data);
42431     }
42432     
42433     this.selectedIndex = -1;
42434     if(this.mode == 'local'){
42435         if(config.queryDelay === undefined){
42436             this.queryDelay = 10;
42437         }
42438         if(config.minChars === undefined){
42439             this.minChars = 0;
42440         }
42441     }
42442 };
42443
42444 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42445     /**
42446      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42447      */
42448     /**
42449      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42450      * rendering into an Roo.Editor, defaults to false)
42451      */
42452     /**
42453      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42454      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42455      */
42456     /**
42457      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42458      */
42459     /**
42460      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42461      * the dropdown list (defaults to undefined, with no header element)
42462      */
42463
42464      /**
42465      * @cfg {String/Roo.Template} tpl The template to use to render the output
42466      */
42467      
42468     // private
42469     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42470     /**
42471      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42472      */
42473     listWidth: undefined,
42474     /**
42475      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42476      * mode = 'remote' or 'text' if mode = 'local')
42477      */
42478     displayField: undefined,
42479     /**
42480      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42481      * mode = 'remote' or 'value' if mode = 'local'). 
42482      * Note: use of a valueField requires the user make a selection
42483      * in order for a value to be mapped.
42484      */
42485     valueField: undefined,
42486     
42487     
42488     /**
42489      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42490      * field's data value (defaults to the underlying DOM element's name)
42491      */
42492     hiddenName: undefined,
42493     /**
42494      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42495      */
42496     listClass: '',
42497     /**
42498      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42499      */
42500     selectedClass: 'x-combo-selected',
42501     /**
42502      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42503      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42504      * which displays a downward arrow icon).
42505      */
42506     triggerClass : 'x-form-arrow-trigger',
42507     /**
42508      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42509      */
42510     shadow:'sides',
42511     /**
42512      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42513      * anchor positions (defaults to 'tl-bl')
42514      */
42515     listAlign: 'tl-bl?',
42516     /**
42517      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42518      */
42519     maxHeight: 300,
42520     /**
42521      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42522      * query specified by the allQuery config option (defaults to 'query')
42523      */
42524     triggerAction: 'query',
42525     /**
42526      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42527      * (defaults to 4, does not apply if editable = false)
42528      */
42529     minChars : 4,
42530     /**
42531      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42532      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42533      */
42534     typeAhead: false,
42535     /**
42536      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42537      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42538      */
42539     queryDelay: 500,
42540     /**
42541      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42542      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42543      */
42544     pageSize: 0,
42545     /**
42546      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42547      * when editable = true (defaults to false)
42548      */
42549     selectOnFocus:false,
42550     /**
42551      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42552      */
42553     queryParam: 'query',
42554     /**
42555      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42556      * when mode = 'remote' (defaults to 'Loading...')
42557      */
42558     loadingText: 'Loading...',
42559     /**
42560      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42561      */
42562     resizable: false,
42563     /**
42564      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42565      */
42566     handleHeight : 8,
42567     /**
42568      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42569      * traditional select (defaults to true)
42570      */
42571     editable: true,
42572     /**
42573      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42574      */
42575     allQuery: '',
42576     /**
42577      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42578      */
42579     mode: 'remote',
42580     /**
42581      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42582      * listWidth has a higher value)
42583      */
42584     minListWidth : 70,
42585     /**
42586      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42587      * allow the user to set arbitrary text into the field (defaults to false)
42588      */
42589     forceSelection:false,
42590     /**
42591      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42592      * if typeAhead = true (defaults to 250)
42593      */
42594     typeAheadDelay : 250,
42595     /**
42596      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42597      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42598      */
42599     valueNotFoundText : undefined,
42600     /**
42601      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42602      */
42603     blockFocus : false,
42604     
42605     /**
42606      * @cfg {Boolean} disableClear Disable showing of clear button.
42607      */
42608     disableClear : false,
42609     /**
42610      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42611      */
42612     alwaysQuery : false,
42613     
42614     //private
42615     addicon : false,
42616     editicon: false,
42617     
42618     // element that contains real text value.. (when hidden is used..)
42619      
42620     // private
42621     onRender : function(ct, position)
42622     {
42623         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42624         
42625         if(this.hiddenName){
42626             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42627                     'before', true);
42628             this.hiddenField.value =
42629                 this.hiddenValue !== undefined ? this.hiddenValue :
42630                 this.value !== undefined ? this.value : '';
42631
42632             // prevent input submission
42633             this.el.dom.removeAttribute('name');
42634              
42635              
42636         }
42637         
42638         if(Roo.isGecko){
42639             this.el.dom.setAttribute('autocomplete', 'off');
42640         }
42641
42642         var cls = 'x-combo-list';
42643
42644         this.list = new Roo.Layer({
42645             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42646         });
42647
42648         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42649         this.list.setWidth(lw);
42650         this.list.swallowEvent('mousewheel');
42651         this.assetHeight = 0;
42652
42653         if(this.title){
42654             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42655             this.assetHeight += this.header.getHeight();
42656         }
42657
42658         this.innerList = this.list.createChild({cls:cls+'-inner'});
42659         this.innerList.on('mouseover', this.onViewOver, this);
42660         this.innerList.on('mousemove', this.onViewMove, this);
42661         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42662         
42663         if(this.allowBlank && !this.pageSize && !this.disableClear){
42664             this.footer = this.list.createChild({cls:cls+'-ft'});
42665             this.pageTb = new Roo.Toolbar(this.footer);
42666            
42667         }
42668         if(this.pageSize){
42669             this.footer = this.list.createChild({cls:cls+'-ft'});
42670             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42671                     {pageSize: this.pageSize});
42672             
42673         }
42674         
42675         if (this.pageTb && this.allowBlank && !this.disableClear) {
42676             var _this = this;
42677             this.pageTb.add(new Roo.Toolbar.Fill(), {
42678                 cls: 'x-btn-icon x-btn-clear',
42679                 text: '&#160;',
42680                 handler: function()
42681                 {
42682                     _this.collapse();
42683                     _this.clearValue();
42684                     _this.onSelect(false, -1);
42685                 }
42686             });
42687         }
42688         if (this.footer) {
42689             this.assetHeight += this.footer.getHeight();
42690         }
42691         
42692
42693         if(!this.tpl){
42694             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42695         }
42696
42697         this.view = new Roo.View(this.innerList, this.tpl, {
42698             singleSelect:true,
42699             store: this.store,
42700             selectedClass: this.selectedClass
42701         });
42702
42703         this.view.on('click', this.onViewClick, this);
42704
42705         this.store.on('beforeload', this.onBeforeLoad, this);
42706         this.store.on('load', this.onLoad, this);
42707         this.store.on('loadexception', this.onLoadException, this);
42708
42709         if(this.resizable){
42710             this.resizer = new Roo.Resizable(this.list,  {
42711                pinned:true, handles:'se'
42712             });
42713             this.resizer.on('resize', function(r, w, h){
42714                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42715                 this.listWidth = w;
42716                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42717                 this.restrictHeight();
42718             }, this);
42719             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42720         }
42721         if(!this.editable){
42722             this.editable = true;
42723             this.setEditable(false);
42724         }  
42725         
42726         
42727         if (typeof(this.events.add.listeners) != 'undefined') {
42728             
42729             this.addicon = this.wrap.createChild(
42730                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42731        
42732             this.addicon.on('click', function(e) {
42733                 this.fireEvent('add', this);
42734             }, this);
42735         }
42736         if (typeof(this.events.edit.listeners) != 'undefined') {
42737             
42738             this.editicon = this.wrap.createChild(
42739                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42740             if (this.addicon) {
42741                 this.editicon.setStyle('margin-left', '40px');
42742             }
42743             this.editicon.on('click', function(e) {
42744                 
42745                 // we fire even  if inothing is selected..
42746                 this.fireEvent('edit', this, this.lastData );
42747                 
42748             }, this);
42749         }
42750         
42751         
42752         
42753     },
42754
42755     // private
42756     initEvents : function(){
42757         Roo.form.ComboBox.superclass.initEvents.call(this);
42758
42759         this.keyNav = new Roo.KeyNav(this.el, {
42760             "up" : function(e){
42761                 this.inKeyMode = true;
42762                 this.selectPrev();
42763             },
42764
42765             "down" : function(e){
42766                 if(!this.isExpanded()){
42767                     this.onTriggerClick();
42768                 }else{
42769                     this.inKeyMode = true;
42770                     this.selectNext();
42771                 }
42772             },
42773
42774             "enter" : function(e){
42775                 this.onViewClick();
42776                 //return true;
42777             },
42778
42779             "esc" : function(e){
42780                 this.collapse();
42781             },
42782
42783             "tab" : function(e){
42784                 this.onViewClick(false);
42785                 this.fireEvent("specialkey", this, e);
42786                 return true;
42787             },
42788
42789             scope : this,
42790
42791             doRelay : function(foo, bar, hname){
42792                 if(hname == 'down' || this.scope.isExpanded()){
42793                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42794                 }
42795                 return true;
42796             },
42797
42798             forceKeyDown: true
42799         });
42800         this.queryDelay = Math.max(this.queryDelay || 10,
42801                 this.mode == 'local' ? 10 : 250);
42802         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42803         if(this.typeAhead){
42804             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42805         }
42806         if(this.editable !== false){
42807             this.el.on("keyup", this.onKeyUp, this);
42808         }
42809         if(this.forceSelection){
42810             this.on('blur', this.doForce, this);
42811         }
42812     },
42813
42814     onDestroy : function(){
42815         if(this.view){
42816             this.view.setStore(null);
42817             this.view.el.removeAllListeners();
42818             this.view.el.remove();
42819             this.view.purgeListeners();
42820         }
42821         if(this.list){
42822             this.list.destroy();
42823         }
42824         if(this.store){
42825             this.store.un('beforeload', this.onBeforeLoad, this);
42826             this.store.un('load', this.onLoad, this);
42827             this.store.un('loadexception', this.onLoadException, this);
42828         }
42829         Roo.form.ComboBox.superclass.onDestroy.call(this);
42830     },
42831
42832     // private
42833     fireKey : function(e){
42834         if(e.isNavKeyPress() && !this.list.isVisible()){
42835             this.fireEvent("specialkey", this, e);
42836         }
42837     },
42838
42839     // private
42840     onResize: function(w, h){
42841         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42842         
42843         if(typeof w != 'number'){
42844             // we do not handle it!?!?
42845             return;
42846         }
42847         var tw = this.trigger.getWidth();
42848         tw += this.addicon ? this.addicon.getWidth() : 0;
42849         tw += this.editicon ? this.editicon.getWidth() : 0;
42850         var x = w - tw;
42851         this.el.setWidth( this.adjustWidth('input', x));
42852             
42853         this.trigger.setStyle('left', x+'px');
42854         
42855         if(this.list && this.listWidth === undefined){
42856             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42857             this.list.setWidth(lw);
42858             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42859         }
42860         
42861     
42862         
42863     },
42864
42865     /**
42866      * Allow or prevent the user from directly editing the field text.  If false is passed,
42867      * the user will only be able to select from the items defined in the dropdown list.  This method
42868      * is the runtime equivalent of setting the 'editable' config option at config time.
42869      * @param {Boolean} value True to allow the user to directly edit the field text
42870      */
42871     setEditable : function(value){
42872         if(value == this.editable){
42873             return;
42874         }
42875         this.editable = value;
42876         if(!value){
42877             this.el.dom.setAttribute('readOnly', true);
42878             this.el.on('mousedown', this.onTriggerClick,  this);
42879             this.el.addClass('x-combo-noedit');
42880         }else{
42881             this.el.dom.setAttribute('readOnly', false);
42882             this.el.un('mousedown', this.onTriggerClick,  this);
42883             this.el.removeClass('x-combo-noedit');
42884         }
42885     },
42886
42887     // private
42888     onBeforeLoad : function(){
42889         if(!this.hasFocus){
42890             return;
42891         }
42892         this.innerList.update(this.loadingText ?
42893                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42894         this.restrictHeight();
42895         this.selectedIndex = -1;
42896     },
42897
42898     // private
42899     onLoad : function(){
42900         if(!this.hasFocus){
42901             return;
42902         }
42903         if(this.store.getCount() > 0){
42904             this.expand();
42905             this.restrictHeight();
42906             if(this.lastQuery == this.allQuery){
42907                 if(this.editable){
42908                     this.el.dom.select();
42909                 }
42910                 if(!this.selectByValue(this.value, true)){
42911                     this.select(0, true);
42912                 }
42913             }else{
42914                 this.selectNext();
42915                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42916                     this.taTask.delay(this.typeAheadDelay);
42917                 }
42918             }
42919         }else{
42920             this.onEmptyResults();
42921         }
42922         //this.el.focus();
42923     },
42924     // private
42925     onLoadException : function()
42926     {
42927         this.collapse();
42928         Roo.log(this.store.reader.jsonData);
42929         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42930             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42931         }
42932         
42933         
42934     },
42935     // private
42936     onTypeAhead : function(){
42937         if(this.store.getCount() > 0){
42938             var r = this.store.getAt(0);
42939             var newValue = r.data[this.displayField];
42940             var len = newValue.length;
42941             var selStart = this.getRawValue().length;
42942             if(selStart != len){
42943                 this.setRawValue(newValue);
42944                 this.selectText(selStart, newValue.length);
42945             }
42946         }
42947     },
42948
42949     // private
42950     onSelect : function(record, index){
42951         if(this.fireEvent('beforeselect', this, record, index) !== false){
42952             this.setFromData(index > -1 ? record.data : false);
42953             this.collapse();
42954             this.fireEvent('select', this, record, index);
42955         }
42956     },
42957
42958     /**
42959      * Returns the currently selected field value or empty string if no value is set.
42960      * @return {String} value The selected value
42961      */
42962     getValue : function(){
42963         if(this.valueField){
42964             return typeof this.value != 'undefined' ? this.value : '';
42965         }
42966         return Roo.form.ComboBox.superclass.getValue.call(this);
42967     },
42968
42969     /**
42970      * Clears any text/value currently set in the field
42971      */
42972     clearValue : function(){
42973         if(this.hiddenField){
42974             this.hiddenField.value = '';
42975         }
42976         this.value = '';
42977         this.setRawValue('');
42978         this.lastSelectionText = '';
42979         
42980     },
42981
42982     /**
42983      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42984      * will be displayed in the field.  If the value does not match the data value of an existing item,
42985      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42986      * Otherwise the field will be blank (although the value will still be set).
42987      * @param {String} value The value to match
42988      */
42989     setValue : function(v){
42990         var text = v;
42991         if(this.valueField){
42992             var r = this.findRecord(this.valueField, v);
42993             if(r){
42994                 text = r.data[this.displayField];
42995             }else if(this.valueNotFoundText !== undefined){
42996                 text = this.valueNotFoundText;
42997             }
42998         }
42999         this.lastSelectionText = text;
43000         if(this.hiddenField){
43001             this.hiddenField.value = v;
43002         }
43003         Roo.form.ComboBox.superclass.setValue.call(this, text);
43004         this.value = v;
43005     },
43006     /**
43007      * @property {Object} the last set data for the element
43008      */
43009     
43010     lastData : false,
43011     /**
43012      * Sets the value of the field based on a object which is related to the record format for the store.
43013      * @param {Object} value the value to set as. or false on reset?
43014      */
43015     setFromData : function(o){
43016         var dv = ''; // display value
43017         var vv = ''; // value value..
43018         this.lastData = o;
43019         if (this.displayField) {
43020             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43021         } else {
43022             // this is an error condition!!!
43023             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43024         }
43025         
43026         if(this.valueField){
43027             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43028         }
43029         if(this.hiddenField){
43030             this.hiddenField.value = vv;
43031             
43032             this.lastSelectionText = dv;
43033             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43034             this.value = vv;
43035             return;
43036         }
43037         // no hidden field.. - we store the value in 'value', but still display
43038         // display field!!!!
43039         this.lastSelectionText = dv;
43040         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43041         this.value = vv;
43042         
43043         
43044     },
43045     // private
43046     reset : function(){
43047         // overridden so that last data is reset..
43048         this.setValue(this.resetValue);
43049         this.originalValue = this.getValue();
43050         this.clearInvalid();
43051         this.lastData = false;
43052         if (this.view) {
43053             this.view.clearSelections();
43054         }
43055     },
43056     // private
43057     findRecord : function(prop, value){
43058         var record;
43059         if(this.store.getCount() > 0){
43060             this.store.each(function(r){
43061                 if(r.data[prop] == value){
43062                     record = r;
43063                     return false;
43064                 }
43065                 return true;
43066             });
43067         }
43068         return record;
43069     },
43070     
43071     getName: function()
43072     {
43073         // returns hidden if it's set..
43074         if (!this.rendered) {return ''};
43075         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43076         
43077     },
43078     // private
43079     onViewMove : function(e, t){
43080         this.inKeyMode = false;
43081     },
43082
43083     // private
43084     onViewOver : function(e, t){
43085         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43086             return;
43087         }
43088         var item = this.view.findItemFromChild(t);
43089         if(item){
43090             var index = this.view.indexOf(item);
43091             this.select(index, false);
43092         }
43093     },
43094
43095     // private
43096     onViewClick : function(doFocus)
43097     {
43098         var index = this.view.getSelectedIndexes()[0];
43099         var r = this.store.getAt(index);
43100         if(r){
43101             this.onSelect(r, index);
43102         }
43103         if(doFocus !== false && !this.blockFocus){
43104             this.el.focus();
43105         }
43106     },
43107
43108     // private
43109     restrictHeight : function(){
43110         this.innerList.dom.style.height = '';
43111         var inner = this.innerList.dom;
43112         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43113         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43114         this.list.beginUpdate();
43115         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43116         this.list.alignTo(this.el, this.listAlign);
43117         this.list.endUpdate();
43118     },
43119
43120     // private
43121     onEmptyResults : function(){
43122         this.collapse();
43123     },
43124
43125     /**
43126      * Returns true if the dropdown list is expanded, else false.
43127      */
43128     isExpanded : function(){
43129         return this.list.isVisible();
43130     },
43131
43132     /**
43133      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43134      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43135      * @param {String} value The data value of the item to select
43136      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43137      * selected item if it is not currently in view (defaults to true)
43138      * @return {Boolean} True if the value matched an item in the list, else false
43139      */
43140     selectByValue : function(v, scrollIntoView){
43141         if(v !== undefined && v !== null){
43142             var r = this.findRecord(this.valueField || this.displayField, v);
43143             if(r){
43144                 this.select(this.store.indexOf(r), scrollIntoView);
43145                 return true;
43146             }
43147         }
43148         return false;
43149     },
43150
43151     /**
43152      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43153      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43154      * @param {Number} index The zero-based index of the list item to select
43155      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43156      * selected item if it is not currently in view (defaults to true)
43157      */
43158     select : function(index, scrollIntoView){
43159         this.selectedIndex = index;
43160         this.view.select(index);
43161         if(scrollIntoView !== false){
43162             var el = this.view.getNode(index);
43163             if(el){
43164                 this.innerList.scrollChildIntoView(el, false);
43165             }
43166         }
43167     },
43168
43169     // private
43170     selectNext : function(){
43171         var ct = this.store.getCount();
43172         if(ct > 0){
43173             if(this.selectedIndex == -1){
43174                 this.select(0);
43175             }else if(this.selectedIndex < ct-1){
43176                 this.select(this.selectedIndex+1);
43177             }
43178         }
43179     },
43180
43181     // private
43182     selectPrev : function(){
43183         var ct = this.store.getCount();
43184         if(ct > 0){
43185             if(this.selectedIndex == -1){
43186                 this.select(0);
43187             }else if(this.selectedIndex != 0){
43188                 this.select(this.selectedIndex-1);
43189             }
43190         }
43191     },
43192
43193     // private
43194     onKeyUp : function(e){
43195         if(this.editable !== false && !e.isSpecialKey()){
43196             this.lastKey = e.getKey();
43197             this.dqTask.delay(this.queryDelay);
43198         }
43199     },
43200
43201     // private
43202     validateBlur : function(){
43203         return !this.list || !this.list.isVisible();   
43204     },
43205
43206     // private
43207     initQuery : function(){
43208         this.doQuery(this.getRawValue());
43209     },
43210
43211     // private
43212     doForce : function(){
43213         if(this.el.dom.value.length > 0){
43214             this.el.dom.value =
43215                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43216              
43217         }
43218     },
43219
43220     /**
43221      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43222      * query allowing the query action to be canceled if needed.
43223      * @param {String} query The SQL query to execute
43224      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43225      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43226      * saved in the current store (defaults to false)
43227      */
43228     doQuery : function(q, forceAll){
43229         if(q === undefined || q === null){
43230             q = '';
43231         }
43232         var qe = {
43233             query: q,
43234             forceAll: forceAll,
43235             combo: this,
43236             cancel:false
43237         };
43238         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43239             return false;
43240         }
43241         q = qe.query;
43242         forceAll = qe.forceAll;
43243         if(forceAll === true || (q.length >= this.minChars)){
43244             if(this.lastQuery != q || this.alwaysQuery){
43245                 this.lastQuery = q;
43246                 if(this.mode == 'local'){
43247                     this.selectedIndex = -1;
43248                     if(forceAll){
43249                         this.store.clearFilter();
43250                     }else{
43251                         this.store.filter(this.displayField, q);
43252                     }
43253                     this.onLoad();
43254                 }else{
43255                     this.store.baseParams[this.queryParam] = q;
43256                     this.store.load({
43257                         params: this.getParams(q)
43258                     });
43259                     this.expand();
43260                 }
43261             }else{
43262                 this.selectedIndex = -1;
43263                 this.onLoad();   
43264             }
43265         }
43266     },
43267
43268     // private
43269     getParams : function(q){
43270         var p = {};
43271         //p[this.queryParam] = q;
43272         if(this.pageSize){
43273             p.start = 0;
43274             p.limit = this.pageSize;
43275         }
43276         return p;
43277     },
43278
43279     /**
43280      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43281      */
43282     collapse : function(){
43283         if(!this.isExpanded()){
43284             return;
43285         }
43286         this.list.hide();
43287         Roo.get(document).un('mousedown', this.collapseIf, this);
43288         Roo.get(document).un('mousewheel', this.collapseIf, this);
43289         if (!this.editable) {
43290             Roo.get(document).un('keydown', this.listKeyPress, this);
43291         }
43292         this.fireEvent('collapse', this);
43293     },
43294
43295     // private
43296     collapseIf : function(e){
43297         if(!e.within(this.wrap) && !e.within(this.list)){
43298             this.collapse();
43299         }
43300     },
43301
43302     /**
43303      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43304      */
43305     expand : function(){
43306         if(this.isExpanded() || !this.hasFocus){
43307             return;
43308         }
43309         this.list.alignTo(this.el, this.listAlign);
43310         this.list.show();
43311         Roo.get(document).on('mousedown', this.collapseIf, this);
43312         Roo.get(document).on('mousewheel', this.collapseIf, this);
43313         if (!this.editable) {
43314             Roo.get(document).on('keydown', this.listKeyPress, this);
43315         }
43316         
43317         this.fireEvent('expand', this);
43318     },
43319
43320     // private
43321     // Implements the default empty TriggerField.onTriggerClick function
43322     onTriggerClick : function(){
43323         if(this.disabled){
43324             return;
43325         }
43326         if(this.isExpanded()){
43327             this.collapse();
43328             if (!this.blockFocus) {
43329                 this.el.focus();
43330             }
43331             
43332         }else {
43333             this.hasFocus = true;
43334             if(this.triggerAction == 'all') {
43335                 this.doQuery(this.allQuery, true);
43336             } else {
43337                 this.doQuery(this.getRawValue());
43338             }
43339             if (!this.blockFocus) {
43340                 this.el.focus();
43341             }
43342         }
43343     },
43344     listKeyPress : function(e)
43345     {
43346         //Roo.log('listkeypress');
43347         // scroll to first matching element based on key pres..
43348         if (e.isSpecialKey()) {
43349             return false;
43350         }
43351         var k = String.fromCharCode(e.getKey()).toUpperCase();
43352         //Roo.log(k);
43353         var match  = false;
43354         var csel = this.view.getSelectedNodes();
43355         var cselitem = false;
43356         if (csel.length) {
43357             var ix = this.view.indexOf(csel[0]);
43358             cselitem  = this.store.getAt(ix);
43359             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43360                 cselitem = false;
43361             }
43362             
43363         }
43364         
43365         this.store.each(function(v) { 
43366             if (cselitem) {
43367                 // start at existing selection.
43368                 if (cselitem.id == v.id) {
43369                     cselitem = false;
43370                 }
43371                 return;
43372             }
43373                 
43374             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43375                 match = this.store.indexOf(v);
43376                 return false;
43377             }
43378         }, this);
43379         
43380         if (match === false) {
43381             return true; // no more action?
43382         }
43383         // scroll to?
43384         this.view.select(match);
43385         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43386         sn.scrollIntoView(sn.dom.parentNode, false);
43387     } 
43388
43389     /** 
43390     * @cfg {Boolean} grow 
43391     * @hide 
43392     */
43393     /** 
43394     * @cfg {Number} growMin 
43395     * @hide 
43396     */
43397     /** 
43398     * @cfg {Number} growMax 
43399     * @hide 
43400     */
43401     /**
43402      * @hide
43403      * @method autoSize
43404      */
43405 });/*
43406  * Copyright(c) 2010-2012, Roo J Solutions Limited
43407  *
43408  * Licence LGPL
43409  *
43410  */
43411
43412 /**
43413  * @class Roo.form.ComboBoxArray
43414  * @extends Roo.form.TextField
43415  * A facebook style adder... for lists of email / people / countries  etc...
43416  * pick multiple items from a combo box, and shows each one.
43417  *
43418  *  Fred [x]  Brian [x]  [Pick another |v]
43419  *
43420  *
43421  *  For this to work: it needs various extra information
43422  *    - normal combo problay has
43423  *      name, hiddenName
43424  *    + displayField, valueField
43425  *
43426  *    For our purpose...
43427  *
43428  *
43429  *   If we change from 'extends' to wrapping...
43430  *   
43431  *  
43432  *
43433  
43434  
43435  * @constructor
43436  * Create a new ComboBoxArray.
43437  * @param {Object} config Configuration options
43438  */
43439  
43440
43441 Roo.form.ComboBoxArray = function(config)
43442 {
43443     this.addEvents({
43444         /**
43445          * @event beforeremove
43446          * Fires before remove the value from the list
43447              * @param {Roo.form.ComboBoxArray} _self This combo box array
43448              * @param {Roo.form.ComboBoxArray.Item} item removed item
43449              */
43450         'beforeremove' : true,
43451         /**
43452          * @event remove
43453          * Fires when remove the value from the list
43454              * @param {Roo.form.ComboBoxArray} _self This combo box array
43455              * @param {Roo.form.ComboBoxArray.Item} item removed item
43456              */
43457         'remove' : true
43458         
43459         
43460     });
43461     
43462     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43463     
43464     this.items = new Roo.util.MixedCollection(false);
43465     
43466     // construct the child combo...
43467     
43468     
43469     
43470     
43471    
43472     
43473 }
43474
43475  
43476 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43477
43478     /**
43479      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43480      */
43481     
43482     lastData : false,
43483     
43484     // behavies liek a hiddne field
43485     inputType:      'hidden',
43486     /**
43487      * @cfg {Number} width The width of the box that displays the selected element
43488      */ 
43489     width:          300,
43490
43491     
43492     
43493     /**
43494      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43495      */
43496     name : false,
43497     /**
43498      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43499      */
43500     hiddenName : false,
43501       /**
43502      * @cfg {String} seperator    The value seperator normally ',' 
43503      */
43504     seperator : ',',
43505     
43506     // private the array of items that are displayed..
43507     items  : false,
43508     // private - the hidden field el.
43509     hiddenEl : false,
43510     // private - the filed el..
43511     el : false,
43512     
43513     //validateValue : function() { return true; }, // all values are ok!
43514     //onAddClick: function() { },
43515     
43516     onRender : function(ct, position) 
43517     {
43518         
43519         // create the standard hidden element
43520         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43521         
43522         
43523         // give fake names to child combo;
43524         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43525         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43526         
43527         this.combo = Roo.factory(this.combo, Roo.form);
43528         this.combo.onRender(ct, position);
43529         if (typeof(this.combo.width) != 'undefined') {
43530             this.combo.onResize(this.combo.width,0);
43531         }
43532         
43533         this.combo.initEvents();
43534         
43535         // assigned so form know we need to do this..
43536         this.store          = this.combo.store;
43537         this.valueField     = this.combo.valueField;
43538         this.displayField   = this.combo.displayField ;
43539         
43540         
43541         this.combo.wrap.addClass('x-cbarray-grp');
43542         
43543         var cbwrap = this.combo.wrap.createChild(
43544             {tag: 'div', cls: 'x-cbarray-cb'},
43545             this.combo.el.dom
43546         );
43547         
43548              
43549         this.hiddenEl = this.combo.wrap.createChild({
43550             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43551         });
43552         this.el = this.combo.wrap.createChild({
43553             tag: 'input',  type:'hidden' , name: this.name, value : ''
43554         });
43555          //   this.el.dom.removeAttribute("name");
43556         
43557         
43558         this.outerWrap = this.combo.wrap;
43559         this.wrap = cbwrap;
43560         
43561         this.outerWrap.setWidth(this.width);
43562         this.outerWrap.dom.removeChild(this.el.dom);
43563         
43564         this.wrap.dom.appendChild(this.el.dom);
43565         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43566         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43567         
43568         this.combo.trigger.setStyle('position','relative');
43569         this.combo.trigger.setStyle('left', '0px');
43570         this.combo.trigger.setStyle('top', '2px');
43571         
43572         this.combo.el.setStyle('vertical-align', 'text-bottom');
43573         
43574         //this.trigger.setStyle('vertical-align', 'top');
43575         
43576         // this should use the code from combo really... on('add' ....)
43577         if (this.adder) {
43578             
43579         
43580             this.adder = this.outerWrap.createChild(
43581                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43582             var _t = this;
43583             this.adder.on('click', function(e) {
43584                 _t.fireEvent('adderclick', this, e);
43585             }, _t);
43586         }
43587         //var _t = this;
43588         //this.adder.on('click', this.onAddClick, _t);
43589         
43590         
43591         this.combo.on('select', function(cb, rec, ix) {
43592             this.addItem(rec.data);
43593             
43594             cb.setValue('');
43595             cb.el.dom.value = '';
43596             //cb.lastData = rec.data;
43597             // add to list
43598             
43599         }, this);
43600         
43601         
43602     },
43603     
43604     
43605     getName: function()
43606     {
43607         // returns hidden if it's set..
43608         if (!this.rendered) {return ''};
43609         return  this.hiddenName ? this.hiddenName : this.name;
43610         
43611     },
43612     
43613     
43614     onResize: function(w, h){
43615         
43616         return;
43617         // not sure if this is needed..
43618         //this.combo.onResize(w,h);
43619         
43620         if(typeof w != 'number'){
43621             // we do not handle it!?!?
43622             return;
43623         }
43624         var tw = this.combo.trigger.getWidth();
43625         tw += this.addicon ? this.addicon.getWidth() : 0;
43626         tw += this.editicon ? this.editicon.getWidth() : 0;
43627         var x = w - tw;
43628         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43629             
43630         this.combo.trigger.setStyle('left', '0px');
43631         
43632         if(this.list && this.listWidth === undefined){
43633             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43634             this.list.setWidth(lw);
43635             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43636         }
43637         
43638     
43639         
43640     },
43641     
43642     addItem: function(rec)
43643     {
43644         var valueField = this.combo.valueField;
43645         var displayField = this.combo.displayField;
43646         
43647         if (this.items.indexOfKey(rec[valueField]) > -1) {
43648             //console.log("GOT " + rec.data.id);
43649             return;
43650         }
43651         
43652         var x = new Roo.form.ComboBoxArray.Item({
43653             //id : rec[this.idField],
43654             data : rec,
43655             displayField : displayField ,
43656             tipField : displayField ,
43657             cb : this
43658         });
43659         // use the 
43660         this.items.add(rec[valueField],x);
43661         // add it before the element..
43662         this.updateHiddenEl();
43663         x.render(this.outerWrap, this.wrap.dom);
43664         // add the image handler..
43665     },
43666     
43667     updateHiddenEl : function()
43668     {
43669         this.validate();
43670         if (!this.hiddenEl) {
43671             return;
43672         }
43673         var ar = [];
43674         var idField = this.combo.valueField;
43675         
43676         this.items.each(function(f) {
43677             ar.push(f.data[idField]);
43678         });
43679         this.hiddenEl.dom.value = ar.join(this.seperator);
43680         this.validate();
43681     },
43682     
43683     reset : function()
43684     {
43685         this.items.clear();
43686         
43687         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43688            el.remove();
43689         });
43690         
43691         this.el.dom.value = '';
43692         if (this.hiddenEl) {
43693             this.hiddenEl.dom.value = '';
43694         }
43695         
43696     },
43697     getValue: function()
43698     {
43699         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43700     },
43701     setValue: function(v) // not a valid action - must use addItems..
43702     {
43703         
43704         this.reset();
43705          
43706         if (this.store.isLocal && (typeof(v) == 'string')) {
43707             // then we can use the store to find the values..
43708             // comma seperated at present.. this needs to allow JSON based encoding..
43709             this.hiddenEl.value  = v;
43710             var v_ar = [];
43711             Roo.each(v.split(this.seperator), function(k) {
43712                 Roo.log("CHECK " + this.valueField + ',' + k);
43713                 var li = this.store.query(this.valueField, k);
43714                 if (!li.length) {
43715                     return;
43716                 }
43717                 var add = {};
43718                 add[this.valueField] = k;
43719                 add[this.displayField] = li.item(0).data[this.displayField];
43720                 
43721                 this.addItem(add);
43722             }, this) 
43723              
43724         }
43725         if (typeof(v) == 'object' ) {
43726             // then let's assume it's an array of objects..
43727             Roo.each(v, function(l) {
43728                 var add = l;
43729                 if (typeof(l) == 'string') {
43730                     add = {};
43731                     add[this.valueField] = l;
43732                     add[this.displayField] = l
43733                 }
43734                 this.addItem(add);
43735             }, this);
43736              
43737         }
43738         
43739         
43740     },
43741     setFromData: function(v)
43742     {
43743         // this recieves an object, if setValues is called.
43744         this.reset();
43745         this.el.dom.value = v[this.displayField];
43746         this.hiddenEl.dom.value = v[this.valueField];
43747         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43748             return;
43749         }
43750         var kv = v[this.valueField];
43751         var dv = v[this.displayField];
43752         kv = typeof(kv) != 'string' ? '' : kv;
43753         dv = typeof(dv) != 'string' ? '' : dv;
43754         
43755         
43756         var keys = kv.split(this.seperator);
43757         var display = dv.split(this.seperator);
43758         for (var i = 0 ; i < keys.length; i++) {
43759             add = {};
43760             add[this.valueField] = keys[i];
43761             add[this.displayField] = display[i];
43762             this.addItem(add);
43763         }
43764       
43765         
43766     },
43767     
43768     /**
43769      * Validates the combox array value
43770      * @return {Boolean} True if the value is valid, else false
43771      */
43772     validate : function(){
43773         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43774             this.clearInvalid();
43775             return true;
43776         }
43777         return false;
43778     },
43779     
43780     validateValue : function(value){
43781         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43782         
43783     },
43784     
43785     /*@
43786      * overide
43787      * 
43788      */
43789     isDirty : function() {
43790         if(this.disabled) {
43791             return false;
43792         }
43793         
43794         try {
43795             var d = Roo.decode(String(this.originalValue));
43796         } catch (e) {
43797             return String(this.getValue()) !== String(this.originalValue);
43798         }
43799         
43800         var originalValue = [];
43801         
43802         for (var i = 0; i < d.length; i++){
43803             originalValue.push(d[i][this.valueField]);
43804         }
43805         
43806         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43807         
43808     }
43809     
43810 });
43811
43812
43813
43814 /**
43815  * @class Roo.form.ComboBoxArray.Item
43816  * @extends Roo.BoxComponent
43817  * A selected item in the list
43818  *  Fred [x]  Brian [x]  [Pick another |v]
43819  * 
43820  * @constructor
43821  * Create a new item.
43822  * @param {Object} config Configuration options
43823  */
43824  
43825 Roo.form.ComboBoxArray.Item = function(config) {
43826     config.id = Roo.id();
43827     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43828 }
43829
43830 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43831     data : {},
43832     cb: false,
43833     displayField : false,
43834     tipField : false,
43835     
43836     
43837     defaultAutoCreate : {
43838         tag: 'div',
43839         cls: 'x-cbarray-item',
43840         cn : [ 
43841             { tag: 'div' },
43842             {
43843                 tag: 'img',
43844                 width:16,
43845                 height : 16,
43846                 src : Roo.BLANK_IMAGE_URL ,
43847                 align: 'center'
43848             }
43849         ]
43850         
43851     },
43852     
43853  
43854     onRender : function(ct, position)
43855     {
43856         Roo.form.Field.superclass.onRender.call(this, ct, position);
43857         
43858         if(!this.el){
43859             var cfg = this.getAutoCreate();
43860             this.el = ct.createChild(cfg, position);
43861         }
43862         
43863         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43864         
43865         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43866             this.cb.renderer(this.data) :
43867             String.format('{0}',this.data[this.displayField]);
43868         
43869             
43870         this.el.child('div').dom.setAttribute('qtip',
43871                         String.format('{0}',this.data[this.tipField])
43872         );
43873         
43874         this.el.child('img').on('click', this.remove, this);
43875         
43876     },
43877    
43878     remove : function()
43879     {
43880         if(this.cb.disabled){
43881             return;
43882         }
43883         
43884         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43885             this.cb.items.remove(this);
43886             this.el.child('img').un('click', this.remove, this);
43887             this.el.remove();
43888             this.cb.updateHiddenEl();
43889
43890             this.cb.fireEvent('remove', this.cb, this);
43891         }
43892         
43893     }
43894 });/*
43895  * RooJS Library 1.1.1
43896  * Copyright(c) 2008-2011  Alan Knowles
43897  *
43898  * License - LGPL
43899  */
43900  
43901
43902 /**
43903  * @class Roo.form.ComboNested
43904  * @extends Roo.form.ComboBox
43905  * A combobox for that allows selection of nested items in a list,
43906  * eg.
43907  *
43908  *  Book
43909  *    -> red
43910  *    -> green
43911  *  Table
43912  *    -> square
43913  *      ->red
43914  *      ->green
43915  *    -> rectangle
43916  *      ->green
43917  *      
43918  * 
43919  * @constructor
43920  * Create a new ComboNested
43921  * @param {Object} config Configuration options
43922  */
43923 Roo.form.ComboNested = function(config){
43924     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43925     // should verify some data...
43926     // like
43927     // hiddenName = required..
43928     // displayField = required
43929     // valudField == required
43930     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43931     var _t = this;
43932     Roo.each(req, function(e) {
43933         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43934             throw "Roo.form.ComboNested : missing value for: " + e;
43935         }
43936     });
43937      
43938     
43939 };
43940
43941 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43942    
43943     /*
43944      * @config {Number} max Number of columns to show
43945      */
43946     
43947     maxColumns : 3,
43948    
43949     list : null, // the outermost div..
43950     innerLists : null, // the
43951     views : null,
43952     stores : null,
43953     // private
43954     loadingChildren : false,
43955     
43956     onRender : function(ct, position)
43957     {
43958         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43959         
43960         if(this.hiddenName){
43961             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43962                     'before', true);
43963             this.hiddenField.value =
43964                 this.hiddenValue !== undefined ? this.hiddenValue :
43965                 this.value !== undefined ? this.value : '';
43966
43967             // prevent input submission
43968             this.el.dom.removeAttribute('name');
43969              
43970              
43971         }
43972         
43973         if(Roo.isGecko){
43974             this.el.dom.setAttribute('autocomplete', 'off');
43975         }
43976
43977         var cls = 'x-combo-list';
43978
43979         this.list = new Roo.Layer({
43980             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43981         });
43982
43983         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43984         this.list.setWidth(lw);
43985         this.list.swallowEvent('mousewheel');
43986         this.assetHeight = 0;
43987
43988         if(this.title){
43989             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43990             this.assetHeight += this.header.getHeight();
43991         }
43992         this.innerLists = [];
43993         this.views = [];
43994         this.stores = [];
43995         for (var i =0 ; i < this.maxColumns; i++) {
43996             this.onRenderList( cls, i);
43997         }
43998         
43999         // always needs footer, as we are going to have an 'OK' button.
44000         this.footer = this.list.createChild({cls:cls+'-ft'});
44001         this.pageTb = new Roo.Toolbar(this.footer);  
44002         var _this = this;
44003         this.pageTb.add(  {
44004             
44005             text: 'Done',
44006             handler: function()
44007             {
44008                 _this.collapse();
44009             }
44010         });
44011         
44012         if ( this.allowBlank && !this.disableClear) {
44013             
44014             this.pageTb.add(new Roo.Toolbar.Fill(), {
44015                 cls: 'x-btn-icon x-btn-clear',
44016                 text: '&#160;',
44017                 handler: function()
44018                 {
44019                     _this.collapse();
44020                     _this.clearValue();
44021                     _this.onSelect(false, -1);
44022                 }
44023             });
44024         }
44025         if (this.footer) {
44026             this.assetHeight += this.footer.getHeight();
44027         }
44028         
44029     },
44030     onRenderList : function (  cls, i)
44031     {
44032         
44033         var lw = Math.floor(
44034                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44035         );
44036         
44037         this.list.setWidth(lw); // default to '1'
44038
44039         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44040         //il.on('mouseover', this.onViewOver, this, { list:  i });
44041         //il.on('mousemove', this.onViewMove, this, { list:  i });
44042         il.setWidth(lw);
44043         il.setStyle({ 'overflow-x' : 'hidden'});
44044
44045         if(!this.tpl){
44046             this.tpl = new Roo.Template({
44047                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44048                 isEmpty: function (value, allValues) {
44049                     //Roo.log(value);
44050                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44051                     return dl ? 'has-children' : 'no-children'
44052                 }
44053             });
44054         }
44055         
44056         var store  = this.store;
44057         if (i > 0) {
44058             store  = new Roo.data.SimpleStore({
44059                 //fields : this.store.reader.meta.fields,
44060                 reader : this.store.reader,
44061                 data : [ ]
44062             });
44063         }
44064         this.stores[i]  = store;
44065                   
44066         var view = this.views[i] = new Roo.View(
44067             il,
44068             this.tpl,
44069             {
44070                 singleSelect:true,
44071                 store: store,
44072                 selectedClass: this.selectedClass
44073             }
44074         );
44075         view.getEl().setWidth(lw);
44076         view.getEl().setStyle({
44077             position: i < 1 ? 'relative' : 'absolute',
44078             top: 0,
44079             left: (i * lw ) + 'px',
44080             display : i > 0 ? 'none' : 'block'
44081         });
44082         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44083         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44084         //view.on('click', this.onViewClick, this, { list : i });
44085
44086         store.on('beforeload', this.onBeforeLoad, this);
44087         store.on('load',  this.onLoad, this, { list  : i});
44088         store.on('loadexception', this.onLoadException, this);
44089
44090         // hide the other vies..
44091         
44092         
44093         
44094     },
44095       
44096     restrictHeight : function()
44097     {
44098         var mh = 0;
44099         Roo.each(this.innerLists, function(il,i) {
44100             var el = this.views[i].getEl();
44101             el.dom.style.height = '';
44102             var inner = el.dom;
44103             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44104             // only adjust heights on other ones..
44105             mh = Math.max(h, mh);
44106             if (i < 1) {
44107                 
44108                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44109                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44110                
44111             }
44112             
44113             
44114         }, this);
44115         
44116         this.list.beginUpdate();
44117         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44118         this.list.alignTo(this.el, this.listAlign);
44119         this.list.endUpdate();
44120         
44121     },
44122      
44123     
44124     // -- store handlers..
44125     // private
44126     onBeforeLoad : function()
44127     {
44128         if(!this.hasFocus){
44129             return;
44130         }
44131         this.innerLists[0].update(this.loadingText ?
44132                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44133         this.restrictHeight();
44134         this.selectedIndex = -1;
44135     },
44136     // private
44137     onLoad : function(a,b,c,d)
44138     {
44139         if (!this.loadingChildren) {
44140             // then we are loading the top level. - hide the children
44141             for (var i = 1;i < this.views.length; i++) {
44142                 this.views[i].getEl().setStyle({ display : 'none' });
44143             }
44144             var lw = Math.floor(
44145                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44146             );
44147         
44148              this.list.setWidth(lw); // default to '1'
44149
44150             
44151         }
44152         if(!this.hasFocus){
44153             return;
44154         }
44155         
44156         if(this.store.getCount() > 0) {
44157             this.expand();
44158             this.restrictHeight();   
44159         } else {
44160             this.onEmptyResults();
44161         }
44162         
44163         if (!this.loadingChildren) {
44164             this.selectActive();
44165         }
44166         /*
44167         this.stores[1].loadData([]);
44168         this.stores[2].loadData([]);
44169         this.views
44170         */    
44171     
44172         //this.el.focus();
44173     },
44174     
44175     
44176     // private
44177     onLoadException : function()
44178     {
44179         this.collapse();
44180         Roo.log(this.store.reader.jsonData);
44181         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44182             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44183         }
44184         
44185         
44186     },
44187     // no cleaning of leading spaces on blur here.
44188     cleanLeadingSpace : function(e) { },
44189     
44190
44191     onSelectChange : function (view, sels, opts )
44192     {
44193         var ix = view.getSelectedIndexes();
44194          
44195         if (opts.list > this.maxColumns - 2) {
44196             if (view.store.getCount()<  1) {
44197                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44198
44199             } else  {
44200                 if (ix.length) {
44201                     // used to clear ?? but if we are loading unselected 
44202                     this.setFromData(view.store.getAt(ix[0]).data);
44203                 }
44204                 
44205             }
44206             
44207             return;
44208         }
44209         
44210         if (!ix.length) {
44211             // this get's fired when trigger opens..
44212            // this.setFromData({});
44213             var str = this.stores[opts.list+1];
44214             str.data.clear(); // removeall wihtout the fire events..
44215             return;
44216         }
44217         
44218         var rec = view.store.getAt(ix[0]);
44219          
44220         this.setFromData(rec.data);
44221         this.fireEvent('select', this, rec, ix[0]);
44222         
44223         var lw = Math.floor(
44224              (
44225                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44226              ) / this.maxColumns
44227         );
44228         this.loadingChildren = true;
44229         this.stores[opts.list+1].loadDataFromChildren( rec );
44230         this.loadingChildren = false;
44231         var dl = this.stores[opts.list+1]. getTotalCount();
44232         
44233         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44234         
44235         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44236         for (var i = opts.list+2; i < this.views.length;i++) {
44237             this.views[i].getEl().setStyle({ display : 'none' });
44238         }
44239         
44240         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44241         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44242         
44243         if (this.isLoading) {
44244            // this.selectActive(opts.list);
44245         }
44246          
44247     },
44248     
44249     
44250     
44251     
44252     onDoubleClick : function()
44253     {
44254         this.collapse(); //??
44255     },
44256     
44257      
44258     
44259     
44260     
44261     // private
44262     recordToStack : function(store, prop, value, stack)
44263     {
44264         var cstore = new Roo.data.SimpleStore({
44265             //fields : this.store.reader.meta.fields, // we need array reader.. for
44266             reader : this.store.reader,
44267             data : [ ]
44268         });
44269         var _this = this;
44270         var record  = false;
44271         var srec = false;
44272         if(store.getCount() < 1){
44273             return false;
44274         }
44275         store.each(function(r){
44276             if(r.data[prop] == value){
44277                 record = r;
44278             srec = r;
44279                 return false;
44280             }
44281             if (r.data.cn && r.data.cn.length) {
44282                 cstore.loadDataFromChildren( r);
44283                 var cret = _this.recordToStack(cstore, prop, value, stack);
44284                 if (cret !== false) {
44285                     record = cret;
44286                     srec = r;
44287                     return false;
44288                 }
44289             }
44290              
44291             return true;
44292         });
44293         if (record == false) {
44294             return false
44295         }
44296         stack.unshift(srec);
44297         return record;
44298     },
44299     
44300     /*
44301      * find the stack of stores that match our value.
44302      *
44303      * 
44304      */
44305     
44306     selectActive : function ()
44307     {
44308         // if store is not loaded, then we will need to wait for that to happen first.
44309         var stack = [];
44310         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44311         for (var i = 0; i < stack.length; i++ ) {
44312             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44313         }
44314         
44315     }
44316         
44317          
44318     
44319     
44320     
44321     
44322 });/*
44323  * Based on:
44324  * Ext JS Library 1.1.1
44325  * Copyright(c) 2006-2007, Ext JS, LLC.
44326  *
44327  * Originally Released Under LGPL - original licence link has changed is not relivant.
44328  *
44329  * Fork - LGPL
44330  * <script type="text/javascript">
44331  */
44332 /**
44333  * @class Roo.form.Checkbox
44334  * @extends Roo.form.Field
44335  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44336  * @constructor
44337  * Creates a new Checkbox
44338  * @param {Object} config Configuration options
44339  */
44340 Roo.form.Checkbox = function(config){
44341     Roo.form.Checkbox.superclass.constructor.call(this, config);
44342     this.addEvents({
44343         /**
44344          * @event check
44345          * Fires when the checkbox is checked or unchecked.
44346              * @param {Roo.form.Checkbox} this This checkbox
44347              * @param {Boolean} checked The new checked value
44348              */
44349         check : true
44350     });
44351 };
44352
44353 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44354     /**
44355      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44356      */
44357     focusClass : undefined,
44358     /**
44359      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44360      */
44361     fieldClass: "x-form-field",
44362     /**
44363      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44364      */
44365     checked: false,
44366     /**
44367      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44368      * {tag: "input", type: "checkbox", autocomplete: "off"})
44369      */
44370     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44371     /**
44372      * @cfg {String} boxLabel The text that appears beside the checkbox
44373      */
44374     boxLabel : "",
44375     /**
44376      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44377      */  
44378     inputValue : '1',
44379     /**
44380      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44381      */
44382      valueOff: '0', // value when not checked..
44383
44384     actionMode : 'viewEl', 
44385     //
44386     // private
44387     itemCls : 'x-menu-check-item x-form-item',
44388     groupClass : 'x-menu-group-item',
44389     inputType : 'hidden',
44390     
44391     
44392     inSetChecked: false, // check that we are not calling self...
44393     
44394     inputElement: false, // real input element?
44395     basedOn: false, // ????
44396     
44397     isFormField: true, // not sure where this is needed!!!!
44398
44399     onResize : function(){
44400         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44401         if(!this.boxLabel){
44402             this.el.alignTo(this.wrap, 'c-c');
44403         }
44404     },
44405
44406     initEvents : function(){
44407         Roo.form.Checkbox.superclass.initEvents.call(this);
44408         this.el.on("click", this.onClick,  this);
44409         this.el.on("change", this.onClick,  this);
44410     },
44411
44412
44413     getResizeEl : function(){
44414         return this.wrap;
44415     },
44416
44417     getPositionEl : function(){
44418         return this.wrap;
44419     },
44420
44421     // private
44422     onRender : function(ct, position){
44423         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44424         /*
44425         if(this.inputValue !== undefined){
44426             this.el.dom.value = this.inputValue;
44427         }
44428         */
44429         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44430         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44431         var viewEl = this.wrap.createChild({ 
44432             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44433         this.viewEl = viewEl;   
44434         this.wrap.on('click', this.onClick,  this); 
44435         
44436         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44437         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44438         
44439         
44440         
44441         if(this.boxLabel){
44442             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44443         //    viewEl.on('click', this.onClick,  this); 
44444         }
44445         //if(this.checked){
44446             this.setChecked(this.checked);
44447         //}else{
44448             //this.checked = this.el.dom;
44449         //}
44450
44451     },
44452
44453     // private
44454     initValue : Roo.emptyFn,
44455
44456     /**
44457      * Returns the checked state of the checkbox.
44458      * @return {Boolean} True if checked, else false
44459      */
44460     getValue : function(){
44461         if(this.el){
44462             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44463         }
44464         return this.valueOff;
44465         
44466     },
44467
44468         // private
44469     onClick : function(){ 
44470         if (this.disabled) {
44471             return;
44472         }
44473         this.setChecked(!this.checked);
44474
44475         //if(this.el.dom.checked != this.checked){
44476         //    this.setValue(this.el.dom.checked);
44477        // }
44478     },
44479
44480     /**
44481      * Sets the checked state of the checkbox.
44482      * On is always based on a string comparison between inputValue and the param.
44483      * @param {Boolean/String} value - the value to set 
44484      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44485      */
44486     setValue : function(v,suppressEvent){
44487         
44488         
44489         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44490         //if(this.el && this.el.dom){
44491         //    this.el.dom.checked = this.checked;
44492         //    this.el.dom.defaultChecked = this.checked;
44493         //}
44494         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44495         //this.fireEvent("check", this, this.checked);
44496     },
44497     // private..
44498     setChecked : function(state,suppressEvent)
44499     {
44500         if (this.inSetChecked) {
44501             this.checked = state;
44502             return;
44503         }
44504         
44505     
44506         if(this.wrap){
44507             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44508         }
44509         this.checked = state;
44510         if(suppressEvent !== true){
44511             this.fireEvent('check', this, state);
44512         }
44513         this.inSetChecked = true;
44514         this.el.dom.value = state ? this.inputValue : this.valueOff;
44515         this.inSetChecked = false;
44516         
44517     },
44518     // handle setting of hidden value by some other method!!?!?
44519     setFromHidden: function()
44520     {
44521         if(!this.el){
44522             return;
44523         }
44524         //console.log("SET FROM HIDDEN");
44525         //alert('setFrom hidden');
44526         this.setValue(this.el.dom.value);
44527     },
44528     
44529     onDestroy : function()
44530     {
44531         if(this.viewEl){
44532             Roo.get(this.viewEl).remove();
44533         }
44534          
44535         Roo.form.Checkbox.superclass.onDestroy.call(this);
44536     },
44537     
44538     setBoxLabel : function(str)
44539     {
44540         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44541     }
44542
44543 });/*
44544  * Based on:
44545  * Ext JS Library 1.1.1
44546  * Copyright(c) 2006-2007, Ext JS, LLC.
44547  *
44548  * Originally Released Under LGPL - original licence link has changed is not relivant.
44549  *
44550  * Fork - LGPL
44551  * <script type="text/javascript">
44552  */
44553  
44554 /**
44555  * @class Roo.form.Radio
44556  * @extends Roo.form.Checkbox
44557  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44558  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44559  * @constructor
44560  * Creates a new Radio
44561  * @param {Object} config Configuration options
44562  */
44563 Roo.form.Radio = function(){
44564     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44565 };
44566 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44567     inputType: 'radio',
44568
44569     /**
44570      * If this radio is part of a group, it will return the selected value
44571      * @return {String}
44572      */
44573     getGroupValue : function(){
44574         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44575     },
44576     
44577     
44578     onRender : function(ct, position){
44579         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44580         
44581         if(this.inputValue !== undefined){
44582             this.el.dom.value = this.inputValue;
44583         }
44584          
44585         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44586         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44587         //var viewEl = this.wrap.createChild({ 
44588         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44589         //this.viewEl = viewEl;   
44590         //this.wrap.on('click', this.onClick,  this); 
44591         
44592         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44593         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44594         
44595         
44596         
44597         if(this.boxLabel){
44598             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44599         //    viewEl.on('click', this.onClick,  this); 
44600         }
44601          if(this.checked){
44602             this.el.dom.checked =   'checked' ;
44603         }
44604          
44605     } 
44606     
44607     
44608 });//<script type="text/javascript">
44609
44610 /*
44611  * Based  Ext JS Library 1.1.1
44612  * Copyright(c) 2006-2007, Ext JS, LLC.
44613  * LGPL
44614  *
44615  */
44616  
44617 /**
44618  * @class Roo.HtmlEditorCore
44619  * @extends Roo.Component
44620  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
44621  *
44622  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
44623  */
44624
44625 Roo.HtmlEditorCore = function(config){
44626     
44627     
44628     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
44629     
44630     
44631     this.addEvents({
44632         /**
44633          * @event initialize
44634          * Fires when the editor is fully initialized (including the iframe)
44635          * @param {Roo.HtmlEditorCore} this
44636          */
44637         initialize: true,
44638         /**
44639          * @event activate
44640          * Fires when the editor is first receives the focus. Any insertion must wait
44641          * until after this event.
44642          * @param {Roo.HtmlEditorCore} this
44643          */
44644         activate: true,
44645          /**
44646          * @event beforesync
44647          * Fires before the textarea is updated with content from the editor iframe. Return false
44648          * to cancel the sync.
44649          * @param {Roo.HtmlEditorCore} this
44650          * @param {String} html
44651          */
44652         beforesync: true,
44653          /**
44654          * @event beforepush
44655          * Fires before the iframe editor is updated with content from the textarea. Return false
44656          * to cancel the push.
44657          * @param {Roo.HtmlEditorCore} this
44658          * @param {String} html
44659          */
44660         beforepush: true,
44661          /**
44662          * @event sync
44663          * Fires when the textarea is updated with content from the editor iframe.
44664          * @param {Roo.HtmlEditorCore} this
44665          * @param {String} html
44666          */
44667         sync: true,
44668          /**
44669          * @event push
44670          * Fires when the iframe editor is updated with content from the textarea.
44671          * @param {Roo.HtmlEditorCore} this
44672          * @param {String} html
44673          */
44674         push: true,
44675         
44676         /**
44677          * @event editorevent
44678          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
44679          * @param {Roo.HtmlEditorCore} this
44680          */
44681         editorevent: true 
44682          
44683         
44684     });
44685     
44686     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
44687     
44688     // defaults : white / black...
44689     this.applyBlacklists();
44690     
44691     
44692     
44693 };
44694
44695
44696 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
44697
44698
44699      /**
44700      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
44701      */
44702     
44703     owner : false,
44704     
44705      /**
44706      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
44707      *                        Roo.resizable.
44708      */
44709     resizable : false,
44710      /**
44711      * @cfg {Number} height (in pixels)
44712      */   
44713     height: 300,
44714    /**
44715      * @cfg {Number} width (in pixels)
44716      */   
44717     width: 500,
44718      /**
44719      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
44720      *         if you are doing an email editor, this probably needs disabling, it's designed
44721      */
44722     autoClean: true,
44723     
44724     /**
44725      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
44726      */
44727     enableBlocks : true,
44728     /**
44729      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
44730      * 
44731      */
44732     stylesheets: false,
44733      /**
44734      * @cfg {String} language default en - language of text (usefull for rtl languages)
44735      * 
44736      */
44737     language: 'en',
44738     
44739     /**
44740      * @cfg {boolean} allowComments - default false - allow comments in HTML source
44741      *          - by default they are stripped - if you are editing email you may need this.
44742      */
44743     allowComments: false,
44744     // id of frame..
44745     frameId: false,
44746     
44747     // private properties
44748     validationEvent : false,
44749     deferHeight: true,
44750     initialized : false,
44751     activated : false,
44752     sourceEditMode : false,
44753     onFocus : Roo.emptyFn,
44754     iframePad:3,
44755     hideMode:'offsets',
44756     
44757     clearUp: true,
44758     
44759     // blacklist + whitelisted elements..
44760     black: false,
44761     white: false,
44762      
44763     bodyCls : '',
44764
44765     
44766     undoManager : false,
44767     /**
44768      * Protected method that will not generally be called directly. It
44769      * is called when the editor initializes the iframe with HTML contents. Override this method if you
44770      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
44771      */
44772     getDocMarkup : function(){
44773         // body styles..
44774         var st = '';
44775         
44776         // inherit styels from page...?? 
44777         if (this.stylesheets === false) {
44778             
44779             Roo.get(document.head).select('style').each(function(node) {
44780                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44781             });
44782             
44783             Roo.get(document.head).select('link').each(function(node) { 
44784                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
44785             });
44786             
44787         } else if (!this.stylesheets.length) {
44788                 // simple..
44789                 st = '<style type="text/css">' +
44790                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44791                    '</style>';
44792         } else {
44793             for (var i in this.stylesheets) {
44794                 if (typeof(this.stylesheets[i]) != 'string') {
44795                     continue;
44796                 }
44797                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
44798             }
44799             
44800         }
44801         
44802         st +=  '<style type="text/css">' +
44803             'IMG { cursor: pointer } ' +
44804         '</style>';
44805         
44806         st += '<meta name="google" content="notranslate">';
44807         
44808         var cls = 'notranslate roo-htmleditor-body';
44809         
44810         if(this.bodyCls.length){
44811             cls += ' ' + this.bodyCls;
44812         }
44813         
44814         return '<html  class="notranslate" translate="no"><head>' + st  +
44815             //<style type="text/css">' +
44816             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
44817             //'</style>' +
44818             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
44819     },
44820
44821     // private
44822     onRender : function(ct, position)
44823     {
44824         var _t = this;
44825         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
44826         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
44827         
44828         
44829         this.el.dom.style.border = '0 none';
44830         this.el.dom.setAttribute('tabIndex', -1);
44831         this.el.addClass('x-hidden hide');
44832         
44833         
44834         
44835         if(Roo.isIE){ // fix IE 1px bogus margin
44836             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
44837         }
44838        
44839         
44840         this.frameId = Roo.id();
44841         
44842          
44843         
44844         var iframe = this.owner.wrap.createChild({
44845             tag: 'iframe',
44846             cls: 'form-control', // bootstrap..
44847             id: this.frameId,
44848             name: this.frameId,
44849             frameBorder : 'no',
44850             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
44851         }, this.el
44852         );
44853         
44854         
44855         this.iframe = iframe.dom;
44856
44857         this.assignDocWin();
44858         
44859         this.doc.designMode = 'on';
44860        
44861         this.doc.open();
44862         this.doc.write(this.getDocMarkup());
44863         this.doc.close();
44864
44865         
44866         var task = { // must defer to wait for browser to be ready
44867             run : function(){
44868                 //console.log("run task?" + this.doc.readyState);
44869                 this.assignDocWin();
44870                 if(this.doc.body || this.doc.readyState == 'complete'){
44871                     try {
44872                         this.doc.designMode="on";
44873                         
44874                     } catch (e) {
44875                         return;
44876                     }
44877                     Roo.TaskMgr.stop(task);
44878                     this.initEditor.defer(10, this);
44879                 }
44880             },
44881             interval : 10,
44882             duration: 10000,
44883             scope: this
44884         };
44885         Roo.TaskMgr.start(task);
44886
44887     },
44888
44889     // private
44890     onResize : function(w, h)
44891     {
44892          Roo.log('resize: ' +w + ',' + h );
44893         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
44894         if(!this.iframe){
44895             return;
44896         }
44897         if(typeof w == 'number'){
44898             
44899             this.iframe.style.width = w + 'px';
44900         }
44901         if(typeof h == 'number'){
44902             
44903             this.iframe.style.height = h + 'px';
44904             if(this.doc){
44905                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
44906             }
44907         }
44908         
44909     },
44910
44911     /**
44912      * Toggles the editor between standard and source edit mode.
44913      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
44914      */
44915     toggleSourceEdit : function(sourceEditMode){
44916         
44917         this.sourceEditMode = sourceEditMode === true;
44918         
44919         if(this.sourceEditMode){
44920  
44921             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
44922             
44923         }else{
44924             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
44925             //this.iframe.className = '';
44926             this.deferFocus();
44927         }
44928         //this.setSize(this.owner.wrap.getSize());
44929         //this.fireEvent('editmodechange', this, this.sourceEditMode);
44930     },
44931
44932     
44933   
44934
44935     /**
44936      * Protected method that will not generally be called directly. If you need/want
44937      * custom HTML cleanup, this is the method you should override.
44938      * @param {String} html The HTML to be cleaned
44939      * return {String} The cleaned HTML
44940      */
44941     cleanHtml : function(html)
44942     {
44943         html = String(html);
44944         if(html.length > 5){
44945             if(Roo.isSafari){ // strip safari nonsense
44946                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
44947             }
44948         }
44949         if(html == '&nbsp;'){
44950             html = '';
44951         }
44952         return html;
44953     },
44954
44955     /**
44956      * HTML Editor -> Textarea
44957      * Protected method that will not generally be called directly. Syncs the contents
44958      * of the editor iframe with the textarea.
44959      */
44960     syncValue : function()
44961     {
44962         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
44963         if(this.initialized){
44964             
44965             this.undoManager.addEvent();
44966
44967             
44968             var bd = (this.doc.body || this.doc.documentElement);
44969            
44970             
44971             var sel = this.win.getSelection();
44972             
44973             var div = document.createElement('div');
44974             div.innerHTML = bd.innerHTML;
44975             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
44976             if (gtx.length > 0) {
44977                 var rm = gtx.item(0).parentNode;
44978                 rm.parentNode.removeChild(rm);
44979             }
44980             
44981            
44982             if (this.enableBlocks) {
44983                 new Roo.htmleditor.FilterBlock({ node : div });
44984             }
44985             //?? tidy?
44986             var tidy = new Roo.htmleditor.TidySerializer({
44987                 inner:  true
44988             });
44989             var html  = tidy.serialize(div);
44990             
44991             
44992             if(Roo.isSafari){
44993                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
44994                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
44995                 if(m && m[1]){
44996                     html = '<div style="'+m[0]+'">' + html + '</div>';
44997                 }
44998             }
44999             html = this.cleanHtml(html);
45000             // fix up the special chars.. normaly like back quotes in word...
45001             // however we do not want to do this with chinese..
45002             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
45003                 
45004                 var cc = match.charCodeAt();
45005
45006                 // Get the character value, handling surrogate pairs
45007                 if (match.length == 2) {
45008                     // It's a surrogate pair, calculate the Unicode code point
45009                     var high = match.charCodeAt(0) - 0xD800;
45010                     var low  = match.charCodeAt(1) - 0xDC00;
45011                     cc = (high * 0x400) + low + 0x10000;
45012                 }  else if (
45013                     (cc >= 0x4E00 && cc < 0xA000 ) ||
45014                     (cc >= 0x3400 && cc < 0x4E00 ) ||
45015                     (cc >= 0xf900 && cc < 0xfb00 )
45016                 ) {
45017                         return match;
45018                 }  
45019          
45020                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
45021                 return "&#" + cc + ";";
45022                 
45023                 
45024             });
45025             
45026             
45027              
45028             if(this.owner.fireEvent('beforesync', this, html) !== false){
45029                 this.el.dom.value = html;
45030                 this.owner.fireEvent('sync', this, html);
45031             }
45032         }
45033     },
45034
45035     /**
45036      * TEXTAREA -> EDITABLE
45037      * Protected method that will not generally be called directly. Pushes the value of the textarea
45038      * into the iframe editor.
45039      */
45040     pushValue : function()
45041     {
45042         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
45043         if(this.initialized){
45044             var v = this.el.dom.value.trim();
45045             
45046             
45047             if(this.owner.fireEvent('beforepush', this, v) !== false){
45048                 var d = (this.doc.body || this.doc.documentElement);
45049                 d.innerHTML = v;
45050                  
45051                 this.el.dom.value = d.innerHTML;
45052                 this.owner.fireEvent('push', this, v);
45053             }
45054             if (this.autoClean) {
45055                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
45056                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
45057             }
45058             if (this.enableBlocks) {
45059                 Roo.htmleditor.Block.initAll(this.doc.body);
45060             }
45061             
45062             this.updateLanguage();
45063             
45064             var lc = this.doc.body.lastChild;
45065             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
45066                 // add an extra line at the end.
45067                 this.doc.body.appendChild(this.doc.createElement('br'));
45068             }
45069             
45070             
45071         }
45072     },
45073
45074     // private
45075     deferFocus : function(){
45076         this.focus.defer(10, this);
45077     },
45078
45079     // doc'ed in Field
45080     focus : function(){
45081         if(this.win && !this.sourceEditMode){
45082             this.win.focus();
45083         }else{
45084             this.el.focus();
45085         }
45086     },
45087     
45088     assignDocWin: function()
45089     {
45090         var iframe = this.iframe;
45091         
45092          if(Roo.isIE){
45093             this.doc = iframe.contentWindow.document;
45094             this.win = iframe.contentWindow;
45095         } else {
45096 //            if (!Roo.get(this.frameId)) {
45097 //                return;
45098 //            }
45099 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45100 //            this.win = Roo.get(this.frameId).dom.contentWindow;
45101             
45102             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
45103                 return;
45104             }
45105             
45106             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45107             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
45108         }
45109     },
45110     
45111     // private
45112     initEditor : function(){
45113         //console.log("INIT EDITOR");
45114         this.assignDocWin();
45115         
45116         
45117         
45118         this.doc.designMode="on";
45119         this.doc.open();
45120         this.doc.write(this.getDocMarkup());
45121         this.doc.close();
45122         
45123         var dbody = (this.doc.body || this.doc.documentElement);
45124         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
45125         // this copies styles from the containing element into thsi one..
45126         // not sure why we need all of this..
45127         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
45128         
45129         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
45130         //ss['background-attachment'] = 'fixed'; // w3c
45131         dbody.bgProperties = 'fixed'; // ie
45132         dbody.setAttribute("translate", "no");
45133         
45134         //Roo.DomHelper.applyStyles(dbody, ss);
45135         Roo.EventManager.on(this.doc, {
45136              
45137             'mouseup': this.onEditorEvent,
45138             'dblclick': this.onEditorEvent,
45139             'click': this.onEditorEvent,
45140             'keyup': this.onEditorEvent,
45141             
45142             buffer:100,
45143             scope: this
45144         });
45145         Roo.EventManager.on(this.doc, {
45146             'paste': this.onPasteEvent,
45147             scope : this
45148         });
45149         if(Roo.isGecko){
45150             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
45151         }
45152         //??? needed???
45153         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
45154             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
45155         }
45156         this.initialized = true;
45157
45158         
45159         // initialize special key events - enter
45160         new Roo.htmleditor.KeyEnter({core : this});
45161         
45162          
45163         
45164         this.owner.fireEvent('initialize', this);
45165         this.pushValue();
45166     },
45167     // this is to prevent a href clicks resulting in a redirect?
45168    
45169     onPasteEvent : function(e,v)
45170     {
45171         // I think we better assume paste is going to be a dirty load of rubish from word..
45172         
45173         // even pasting into a 'email version' of this widget will have to clean up that mess.
45174         var cd = (e.browserEvent.clipboardData || window.clipboardData);
45175         
45176         // check what type of paste - if it's an image, then handle it differently.
45177         if (cd.files.length > 0) {
45178             // pasting images?
45179             var urlAPI = (window.createObjectURL && window) || 
45180                 (window.URL && URL.revokeObjectURL && URL) || 
45181                 (window.webkitURL && webkitURL);
45182     
45183             var url = urlAPI.createObjectURL( cd.files[0]);
45184             this.insertAtCursor('<img src=" + url + ">');
45185             return false;
45186         }
45187         
45188         var html = cd.getData('text/html'); // clipboard event
45189         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
45190         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
45191         Roo.log(images);
45192         //Roo.log(imgs);
45193         // fixme..
45194         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
45195                        .map(function(g) { return g.toDataURL(); })
45196                        .filter(function(g) { return g != 'about:blank'; });
45197         
45198         
45199         html = this.cleanWordChars(html);
45200         
45201         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
45202         
45203         
45204         var sn = this.getParentElement();
45205         // check if d contains a table, and prevent nesting??
45206         //Roo.log(d.getElementsByTagName('table'));
45207         //Roo.log(sn);
45208         //Roo.log(sn.closest('table'));
45209         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
45210             e.preventDefault();
45211             this.insertAtCursor("You can not nest tables");
45212             //Roo.log("prevent?"); // fixme - 
45213             return false;
45214         }
45215         
45216         if (images.length > 0) {
45217             Roo.each(d.getElementsByTagName('img'), function(img, i) {
45218                 img.setAttribute('src', images[i]);
45219             });
45220         }
45221         if (this.autoClean) {
45222             new Roo.htmleditor.FilterStyleToTag({ node : d });
45223             new Roo.htmleditor.FilterAttributes({
45224                 node : d,
45225                 attrib_white : ['href', 'src', 'name', 'align'],
45226                 attrib_clean : ['href', 'src' ] 
45227             });
45228             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
45229             // should be fonts..
45230             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
45231             new Roo.htmleditor.FilterParagraph({ node : d });
45232             new Roo.htmleditor.FilterSpan({ node : d });
45233             new Roo.htmleditor.FilterLongBr({ node : d });
45234         }
45235         if (this.enableBlocks) {
45236                 
45237             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
45238                 if (img.closest('figure')) { // assume!! that it's aready
45239                     return;
45240                 }
45241                 var fig  = new Roo.htmleditor.BlockFigure({
45242                     image_src  : img.src
45243                 });
45244                 fig.updateElement(img); // replace it..
45245                 
45246             });
45247         }
45248         
45249         
45250         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
45251         if (this.enableBlocks) {
45252             Roo.htmleditor.Block.initAll(this.doc.body);
45253         }
45254         
45255         
45256         e.preventDefault();
45257         return false;
45258         // default behaveiour should be our local cleanup paste? (optional?)
45259         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
45260         //this.owner.fireEvent('paste', e, v);
45261     },
45262     // private
45263     onDestroy : function(){
45264         
45265         
45266         
45267         if(this.rendered){
45268             
45269             //for (var i =0; i < this.toolbars.length;i++) {
45270             //    // fixme - ask toolbars for heights?
45271             //    this.toolbars[i].onDestroy();
45272            // }
45273             
45274             //this.wrap.dom.innerHTML = '';
45275             //this.wrap.remove();
45276         }
45277     },
45278
45279     // private
45280     onFirstFocus : function(){
45281         
45282         this.assignDocWin();
45283         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
45284         
45285         this.activated = true;
45286          
45287     
45288         if(Roo.isGecko){ // prevent silly gecko errors
45289             this.win.focus();
45290             var s = this.win.getSelection();
45291             if(!s.focusNode || s.focusNode.nodeType != 3){
45292                 var r = s.getRangeAt(0);
45293                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
45294                 r.collapse(true);
45295                 this.deferFocus();
45296             }
45297             try{
45298                 this.execCmd('useCSS', true);
45299                 this.execCmd('styleWithCSS', false);
45300             }catch(e){}
45301         }
45302         this.owner.fireEvent('activate', this);
45303     },
45304
45305     // private
45306     adjustFont: function(btn){
45307         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
45308         //if(Roo.isSafari){ // safari
45309         //    adjust *= 2;
45310        // }
45311         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
45312         if(Roo.isSafari){ // safari
45313             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
45314             v =  (v < 10) ? 10 : v;
45315             v =  (v > 48) ? 48 : v;
45316             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
45317             
45318         }
45319         
45320         
45321         v = Math.max(1, v+adjust);
45322         
45323         this.execCmd('FontSize', v  );
45324     },
45325
45326     onEditorEvent : function(e)
45327     {
45328          
45329         
45330         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
45331             return; // we do not handle this.. (undo manager does..)
45332         }
45333         // in theory this detects if the last element is not a br, then we try and do that.
45334         // its so clicking in space at bottom triggers adding a br and moving the cursor.
45335         if (e &&
45336             e.target.nodeName == 'BODY' &&
45337             e.type == "mouseup" &&
45338             this.doc.body.lastChild
45339            ) {
45340             var lc = this.doc.body.lastChild;
45341             // gtx-trans is google translate plugin adding crap.
45342             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
45343                 lc = lc.previousSibling;
45344             }
45345             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
45346             // if last element is <BR> - then dont do anything.
45347             
45348                 var ns = this.doc.createElement('br');
45349                 this.doc.body.appendChild(ns);
45350                 range = this.doc.createRange();
45351                 range.setStartAfter(ns);
45352                 range.collapse(true);
45353                 var sel = this.win.getSelection();
45354                 sel.removeAllRanges();
45355                 sel.addRange(range);
45356             }
45357         }
45358         
45359         
45360         
45361         this.fireEditorEvent(e);
45362       //  this.updateToolbar();
45363         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
45364     },
45365     
45366     fireEditorEvent: function(e)
45367     {
45368         this.owner.fireEvent('editorevent', this, e);
45369     },
45370
45371     insertTag : function(tg)
45372     {
45373         // could be a bit smarter... -> wrap the current selected tRoo..
45374         if (tg.toLowerCase() == 'span' ||
45375             tg.toLowerCase() == 'code' ||
45376             tg.toLowerCase() == 'sup' ||
45377             tg.toLowerCase() == 'sub' 
45378             ) {
45379             
45380             range = this.createRange(this.getSelection());
45381             var wrappingNode = this.doc.createElement(tg.toLowerCase());
45382             wrappingNode.appendChild(range.extractContents());
45383             range.insertNode(wrappingNode);
45384
45385             return;
45386             
45387             
45388             
45389         }
45390         this.execCmd("formatblock",   tg);
45391         this.undoManager.addEvent(); 
45392     },
45393     
45394     insertText : function(txt)
45395     {
45396         
45397         
45398         var range = this.createRange();
45399         range.deleteContents();
45400                //alert(Sender.getAttribute('label'));
45401                
45402         range.insertNode(this.doc.createTextNode(txt));
45403         this.undoManager.addEvent();
45404     } ,
45405     
45406      
45407
45408     /**
45409      * Executes a Midas editor command on the editor document and performs necessary focus and
45410      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
45411      * @param {String} cmd The Midas command
45412      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45413      */
45414     relayCmd : function(cmd, value)
45415     {
45416         
45417         switch (cmd) {
45418             case 'justifyleft':
45419             case 'justifyright':
45420             case 'justifycenter':
45421                 // if we are in a cell, then we will adjust the
45422                 var n = this.getParentElement();
45423                 var td = n.closest('td');
45424                 if (td) {
45425                     var bl = Roo.htmleditor.Block.factory(td);
45426                     bl.textAlign = cmd.replace('justify','');
45427                     bl.updateElement();
45428                     this.owner.fireEvent('editorevent', this);
45429                     return;
45430                 }
45431                 this.execCmd('styleWithCSS', true); // 
45432                 break;
45433             case 'bold':
45434             case 'italic':
45435                 // if there is no selection, then we insert, and set the curson inside it..
45436                 this.execCmd('styleWithCSS', false); 
45437                 break;
45438                 
45439         
45440             default:
45441                 break;
45442         }
45443         
45444         
45445         this.win.focus();
45446         this.execCmd(cmd, value);
45447         this.owner.fireEvent('editorevent', this);
45448         //this.updateToolbar();
45449         this.owner.deferFocus();
45450     },
45451
45452     /**
45453      * Executes a Midas editor command directly on the editor document.
45454      * For visual commands, you should use {@link #relayCmd} instead.
45455      * <b>This should only be called after the editor is initialized.</b>
45456      * @param {String} cmd The Midas command
45457      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45458      */
45459     execCmd : function(cmd, value){
45460         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45461         this.syncValue();
45462     },
45463  
45464  
45465    
45466     /**
45467      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45468      * to insert tRoo.
45469      * @param {String} text | dom node.. 
45470      */
45471     insertAtCursor : function(text)
45472     {
45473         
45474         if(!this.activated){
45475             return;
45476         }
45477          
45478         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
45479             this.win.focus();
45480             
45481             
45482             // from jquery ui (MIT licenced)
45483             var range, node;
45484             var win = this.win;
45485             
45486             if (win.getSelection && win.getSelection().getRangeAt) {
45487                 
45488                 // delete the existing?
45489                 
45490                 this.createRange(this.getSelection()).deleteContents();
45491                 range = win.getSelection().getRangeAt(0);
45492                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
45493                 range.insertNode(node);
45494                 range = range.cloneRange();
45495                 range.collapse(false);
45496                  
45497                 win.getSelection().removeAllRanges();
45498                 win.getSelection().addRange(range);
45499                 
45500                 
45501                 
45502             } else if (win.document.selection && win.document.selection.createRange) {
45503                 // no firefox support
45504                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45505                 win.document.selection.createRange().pasteHTML(txt);
45506             
45507             } else {
45508                 // no firefox support
45509                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45510                 this.execCmd('InsertHTML', txt);
45511             } 
45512             this.syncValue();
45513             
45514             this.deferFocus();
45515         }
45516     },
45517  // private
45518     mozKeyPress : function(e){
45519         if(e.ctrlKey){
45520             var c = e.getCharCode(), cmd;
45521           
45522             if(c > 0){
45523                 c = String.fromCharCode(c).toLowerCase();
45524                 switch(c){
45525                     case 'b':
45526                         cmd = 'bold';
45527                         break;
45528                     case 'i':
45529                         cmd = 'italic';
45530                         break;
45531                     
45532                     case 'u':
45533                         cmd = 'underline';
45534                         break;
45535                     
45536                     //case 'v':
45537                       //  this.cleanUpPaste.defer(100, this);
45538                       //  return;
45539                         
45540                 }
45541                 if(cmd){
45542                     
45543                     this.relayCmd(cmd);
45544                     //this.win.focus();
45545                     //this.execCmd(cmd);
45546                     //this.deferFocus();
45547                     e.preventDefault();
45548                 }
45549                 
45550             }
45551         }
45552     },
45553
45554     // private
45555     fixKeys : function(){ // load time branching for fastest keydown performance
45556         
45557         
45558         if(Roo.isIE){
45559             return function(e){
45560                 var k = e.getKey(), r;
45561                 if(k == e.TAB){
45562                     e.stopEvent();
45563                     r = this.doc.selection.createRange();
45564                     if(r){
45565                         r.collapse(true);
45566                         r.pasteHTML('&#160;&#160;&#160;&#160;');
45567                         this.deferFocus();
45568                     }
45569                     return;
45570                 }
45571                 /// this is handled by Roo.htmleditor.KeyEnter
45572                  /*
45573                 if(k == e.ENTER){
45574                     r = this.doc.selection.createRange();
45575                     if(r){
45576                         var target = r.parentElement();
45577                         if(!target || target.tagName.toLowerCase() != 'li'){
45578                             e.stopEvent();
45579                             r.pasteHTML('<br/>');
45580                             r.collapse(false);
45581                             r.select();
45582                         }
45583                     }
45584                 }
45585                 */
45586                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45587                 //    this.cleanUpPaste.defer(100, this);
45588                 //    return;
45589                 //}
45590                 
45591                 
45592             };
45593         }else if(Roo.isOpera){
45594             return function(e){
45595                 var k = e.getKey();
45596                 if(k == e.TAB){
45597                     e.stopEvent();
45598                     this.win.focus();
45599                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
45600                     this.deferFocus();
45601                 }
45602                
45603                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45604                 //    this.cleanUpPaste.defer(100, this);
45605                  //   return;
45606                 //}
45607                 
45608             };
45609         }else if(Roo.isSafari){
45610             return function(e){
45611                 var k = e.getKey();
45612                 
45613                 if(k == e.TAB){
45614                     e.stopEvent();
45615                     this.execCmd('InsertText','\t');
45616                     this.deferFocus();
45617                     return;
45618                 }
45619                  this.mozKeyPress(e);
45620                 
45621                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45622                  //   this.cleanUpPaste.defer(100, this);
45623                  //   return;
45624                // }
45625                 
45626              };
45627         }
45628     }(),
45629     
45630     getAllAncestors: function()
45631     {
45632         var p = this.getSelectedNode();
45633         var a = [];
45634         if (!p) {
45635             a.push(p); // push blank onto stack..
45636             p = this.getParentElement();
45637         }
45638         
45639         
45640         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
45641             a.push(p);
45642             p = p.parentNode;
45643         }
45644         a.push(this.doc.body);
45645         return a;
45646     },
45647     lastSel : false,
45648     lastSelNode : false,
45649     
45650     
45651     getSelection : function() 
45652     {
45653         this.assignDocWin();
45654         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
45655     },
45656     /**
45657      * Select a dom node
45658      * @param {DomElement} node the node to select
45659      */
45660     selectNode : function(node, collapse)
45661     {
45662         var nodeRange = node.ownerDocument.createRange();
45663         try {
45664             nodeRange.selectNode(node);
45665         } catch (e) {
45666             nodeRange.selectNodeContents(node);
45667         }
45668         if (collapse === true) {
45669             nodeRange.collapse(true);
45670         }
45671         //
45672         var s = this.win.getSelection();
45673         s.removeAllRanges();
45674         s.addRange(nodeRange);
45675     },
45676     
45677     getSelectedNode: function() 
45678     {
45679         // this may only work on Gecko!!!
45680         
45681         // should we cache this!!!!
45682         
45683          
45684          
45685         var range = this.createRange(this.getSelection()).cloneRange();
45686         
45687         if (Roo.isIE) {
45688             var parent = range.parentElement();
45689             while (true) {
45690                 var testRange = range.duplicate();
45691                 testRange.moveToElementText(parent);
45692                 if (testRange.inRange(range)) {
45693                     break;
45694                 }
45695                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
45696                     break;
45697                 }
45698                 parent = parent.parentElement;
45699             }
45700             return parent;
45701         }
45702         
45703         // is ancestor a text element.
45704         var ac =  range.commonAncestorContainer;
45705         if (ac.nodeType == 3) {
45706             ac = ac.parentNode;
45707         }
45708         
45709         var ar = ac.childNodes;
45710          
45711         var nodes = [];
45712         var other_nodes = [];
45713         var has_other_nodes = false;
45714         for (var i=0;i<ar.length;i++) {
45715             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
45716                 continue;
45717             }
45718             // fullly contained node.
45719             
45720             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
45721                 nodes.push(ar[i]);
45722                 continue;
45723             }
45724             
45725             // probably selected..
45726             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
45727                 other_nodes.push(ar[i]);
45728                 continue;
45729             }
45730             // outer..
45731             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
45732                 continue;
45733             }
45734             
45735             
45736             has_other_nodes = true;
45737         }
45738         if (!nodes.length && other_nodes.length) {
45739             nodes= other_nodes;
45740         }
45741         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
45742             return false;
45743         }
45744         
45745         return nodes[0];
45746     },
45747     
45748     
45749     createRange: function(sel)
45750     {
45751         // this has strange effects when using with 
45752         // top toolbar - not sure if it's a great idea.
45753         //this.editor.contentWindow.focus();
45754         if (typeof sel != "undefined") {
45755             try {
45756                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
45757             } catch(e) {
45758                 return this.doc.createRange();
45759             }
45760         } else {
45761             return this.doc.createRange();
45762         }
45763     },
45764     getParentElement: function()
45765     {
45766         
45767         this.assignDocWin();
45768         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
45769         
45770         var range = this.createRange(sel);
45771          
45772         try {
45773             var p = range.commonAncestorContainer;
45774             while (p.nodeType == 3) { // text node
45775                 p = p.parentNode;
45776             }
45777             return p;
45778         } catch (e) {
45779             return null;
45780         }
45781     
45782     },
45783     /***
45784      *
45785      * Range intersection.. the hard stuff...
45786      *  '-1' = before
45787      *  '0' = hits..
45788      *  '1' = after.
45789      *         [ -- selected range --- ]
45790      *   [fail]                        [fail]
45791      *
45792      *    basically..
45793      *      if end is before start or  hits it. fail.
45794      *      if start is after end or hits it fail.
45795      *
45796      *   if either hits (but other is outside. - then it's not 
45797      *   
45798      *    
45799      **/
45800     
45801     
45802     // @see http://www.thismuchiknow.co.uk/?p=64.
45803     rangeIntersectsNode : function(range, node)
45804     {
45805         var nodeRange = node.ownerDocument.createRange();
45806         try {
45807             nodeRange.selectNode(node);
45808         } catch (e) {
45809             nodeRange.selectNodeContents(node);
45810         }
45811     
45812         var rangeStartRange = range.cloneRange();
45813         rangeStartRange.collapse(true);
45814     
45815         var rangeEndRange = range.cloneRange();
45816         rangeEndRange.collapse(false);
45817     
45818         var nodeStartRange = nodeRange.cloneRange();
45819         nodeStartRange.collapse(true);
45820     
45821         var nodeEndRange = nodeRange.cloneRange();
45822         nodeEndRange.collapse(false);
45823     
45824         return rangeStartRange.compareBoundaryPoints(
45825                  Range.START_TO_START, nodeEndRange) == -1 &&
45826                rangeEndRange.compareBoundaryPoints(
45827                  Range.START_TO_START, nodeStartRange) == 1;
45828         
45829          
45830     },
45831     rangeCompareNode : function(range, node)
45832     {
45833         var nodeRange = node.ownerDocument.createRange();
45834         try {
45835             nodeRange.selectNode(node);
45836         } catch (e) {
45837             nodeRange.selectNodeContents(node);
45838         }
45839         
45840         
45841         range.collapse(true);
45842     
45843         nodeRange.collapse(true);
45844      
45845         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
45846         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
45847          
45848         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
45849         
45850         var nodeIsBefore   =  ss == 1;
45851         var nodeIsAfter    = ee == -1;
45852         
45853         if (nodeIsBefore && nodeIsAfter) {
45854             return 0; // outer
45855         }
45856         if (!nodeIsBefore && nodeIsAfter) {
45857             return 1; //right trailed.
45858         }
45859         
45860         if (nodeIsBefore && !nodeIsAfter) {
45861             return 2;  // left trailed.
45862         }
45863         // fully contined.
45864         return 3;
45865     },
45866  
45867     cleanWordChars : function(input) {// change the chars to hex code
45868         
45869        var swapCodes  = [ 
45870             [    8211, "&#8211;" ], 
45871             [    8212, "&#8212;" ], 
45872             [    8216,  "'" ],  
45873             [    8217, "'" ],  
45874             [    8220, '"' ],  
45875             [    8221, '"' ],  
45876             [    8226, "*" ],  
45877             [    8230, "..." ]
45878         ]; 
45879         var output = input;
45880         Roo.each(swapCodes, function(sw) { 
45881             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
45882             
45883             output = output.replace(swapper, sw[1]);
45884         });
45885         
45886         return output;
45887     },
45888     
45889      
45890     
45891         
45892     
45893     cleanUpChild : function (node)
45894     {
45895         
45896         new Roo.htmleditor.FilterComment({node : node});
45897         new Roo.htmleditor.FilterAttributes({
45898                 node : node,
45899                 attrib_black : this.ablack,
45900                 attrib_clean : this.aclean,
45901                 style_white : this.cwhite,
45902                 style_black : this.cblack
45903         });
45904         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
45905         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
45906          
45907         
45908     },
45909     
45910     /**
45911      * Clean up MS wordisms...
45912      * @deprecated - use filter directly
45913      */
45914     cleanWord : function(node)
45915     {
45916         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
45917         
45918     },
45919    
45920     
45921     /**
45922
45923      * @deprecated - use filters
45924      */
45925     cleanTableWidths : function(node)
45926     {
45927         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
45928         
45929  
45930     },
45931     
45932      
45933         
45934     applyBlacklists : function()
45935     {
45936         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45937         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45938         
45939         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
45940         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
45941         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
45942         
45943         this.white = [];
45944         this.black = [];
45945         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45946             if (b.indexOf(tag) > -1) {
45947                 return;
45948             }
45949             this.white.push(tag);
45950             
45951         }, this);
45952         
45953         Roo.each(w, function(tag) {
45954             if (b.indexOf(tag) > -1) {
45955                 return;
45956             }
45957             if (this.white.indexOf(tag) > -1) {
45958                 return;
45959             }
45960             this.white.push(tag);
45961             
45962         }, this);
45963         
45964         
45965         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45966             if (w.indexOf(tag) > -1) {
45967                 return;
45968             }
45969             this.black.push(tag);
45970             
45971         }, this);
45972         
45973         Roo.each(b, function(tag) {
45974             if (w.indexOf(tag) > -1) {
45975                 return;
45976             }
45977             if (this.black.indexOf(tag) > -1) {
45978                 return;
45979             }
45980             this.black.push(tag);
45981             
45982         }, this);
45983         
45984         
45985         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45986         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45987         
45988         this.cwhite = [];
45989         this.cblack = [];
45990         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45991             if (b.indexOf(tag) > -1) {
45992                 return;
45993             }
45994             this.cwhite.push(tag);
45995             
45996         }, this);
45997         
45998         Roo.each(w, function(tag) {
45999             if (b.indexOf(tag) > -1) {
46000                 return;
46001             }
46002             if (this.cwhite.indexOf(tag) > -1) {
46003                 return;
46004             }
46005             this.cwhite.push(tag);
46006             
46007         }, this);
46008         
46009         
46010         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46011             if (w.indexOf(tag) > -1) {
46012                 return;
46013             }
46014             this.cblack.push(tag);
46015             
46016         }, this);
46017         
46018         Roo.each(b, function(tag) {
46019             if (w.indexOf(tag) > -1) {
46020                 return;
46021             }
46022             if (this.cblack.indexOf(tag) > -1) {
46023                 return;
46024             }
46025             this.cblack.push(tag);
46026             
46027         }, this);
46028     },
46029     
46030     setStylesheets : function(stylesheets)
46031     {
46032         if(typeof(stylesheets) == 'string'){
46033             Roo.get(this.iframe.contentDocument.head).createChild({
46034                 tag : 'link',
46035                 rel : 'stylesheet',
46036                 type : 'text/css',
46037                 href : stylesheets
46038             });
46039             
46040             return;
46041         }
46042         var _this = this;
46043      
46044         Roo.each(stylesheets, function(s) {
46045             if(!s.length){
46046                 return;
46047             }
46048             
46049             Roo.get(_this.iframe.contentDocument.head).createChild({
46050                 tag : 'link',
46051                 rel : 'stylesheet',
46052                 type : 'text/css',
46053                 href : s
46054             });
46055         });
46056
46057         
46058     },
46059     
46060     
46061     updateLanguage : function()
46062     {
46063         if (!this.iframe || !this.iframe.contentDocument) {
46064             return;
46065         }
46066         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
46067     },
46068     
46069     
46070     removeStylesheets : function()
46071     {
46072         var _this = this;
46073         
46074         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46075             s.remove();
46076         });
46077     },
46078     
46079     setStyle : function(style)
46080     {
46081         Roo.get(this.iframe.contentDocument.head).createChild({
46082             tag : 'style',
46083             type : 'text/css',
46084             html : style
46085         });
46086
46087         return;
46088     }
46089     
46090     // hide stuff that is not compatible
46091     /**
46092      * @event blur
46093      * @hide
46094      */
46095     /**
46096      * @event change
46097      * @hide
46098      */
46099     /**
46100      * @event focus
46101      * @hide
46102      */
46103     /**
46104      * @event specialkey
46105      * @hide
46106      */
46107     /**
46108      * @cfg {String} fieldClass @hide
46109      */
46110     /**
46111      * @cfg {String} focusClass @hide
46112      */
46113     /**
46114      * @cfg {String} autoCreate @hide
46115      */
46116     /**
46117      * @cfg {String} inputType @hide
46118      */
46119     /**
46120      * @cfg {String} invalidClass @hide
46121      */
46122     /**
46123      * @cfg {String} invalidText @hide
46124      */
46125     /**
46126      * @cfg {String} msgFx @hide
46127      */
46128     /**
46129      * @cfg {String} validateOnBlur @hide
46130      */
46131 });
46132
46133 Roo.HtmlEditorCore.white = [
46134         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
46135         
46136        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
46137        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
46138        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
46139        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
46140        'TABLE',   'UL',         'XMP', 
46141        
46142        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
46143       'THEAD',   'TR', 
46144      
46145       'DIR', 'MENU', 'OL', 'UL', 'DL',
46146        
46147       'EMBED',  'OBJECT'
46148 ];
46149
46150
46151 Roo.HtmlEditorCore.black = [
46152     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46153         'APPLET', // 
46154         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
46155         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
46156         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
46157         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
46158         //'FONT' // CLEAN LATER..
46159         'COLGROUP', 'COL'   // messy tables.
46160         
46161         
46162 ];
46163 Roo.HtmlEditorCore.clean = [ // ?? needed???
46164      'SCRIPT', 'STYLE', 'TITLE', 'XML'
46165 ];
46166 Roo.HtmlEditorCore.tag_remove = [
46167     'FONT', 'TBODY'  
46168 ];
46169 // attributes..
46170
46171 Roo.HtmlEditorCore.ablack = [
46172     'on'
46173 ];
46174     
46175 Roo.HtmlEditorCore.aclean = [ 
46176     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46177 ];
46178
46179 // protocols..
46180 Roo.HtmlEditorCore.pwhite= [
46181         'http',  'https',  'mailto'
46182 ];
46183
46184 // white listed style attributes.
46185 Roo.HtmlEditorCore.cwhite= [
46186       //  'text-align', /// default is to allow most things..
46187       
46188          
46189 //        'font-size'//??
46190 ];
46191
46192 // black listed style attributes.
46193 Roo.HtmlEditorCore.cblack= [
46194       //  'font-size' -- this can be set by the project 
46195 ];
46196
46197
46198
46199
46200     //<script type="text/javascript">
46201
46202 /*
46203  * Ext JS Library 1.1.1
46204  * Copyright(c) 2006-2007, Ext JS, LLC.
46205  * Licence LGPL
46206  * 
46207  */
46208  
46209  
46210 Roo.form.HtmlEditor = function(config){
46211     
46212     
46213     
46214     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46215     
46216     if (!this.toolbars) {
46217         this.toolbars = [];
46218     }
46219     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46220     
46221     
46222 };
46223
46224 /**
46225  * @class Roo.form.HtmlEditor
46226  * @extends Roo.form.Field
46227  * Provides a lightweight HTML Editor component.
46228  *
46229  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46230  * 
46231  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46232  * supported by this editor.</b><br/><br/>
46233  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46234  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46235  */
46236 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46237     /**
46238      * @cfg {Boolean} clearUp
46239      */
46240     clearUp : true,
46241       /**
46242      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46243      */
46244     toolbars : false,
46245    
46246      /**
46247      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46248      *                        Roo.resizable.
46249      */
46250     resizable : false,
46251      /**
46252      * @cfg {Number} height (in pixels)
46253      */   
46254     height: 300,
46255    /**
46256      * @cfg {Number} width (in pixels)
46257      */   
46258     width: 500,
46259     
46260     /**
46261      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
46262      * 
46263      */
46264     stylesheets: false,
46265     
46266     
46267      /**
46268      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46269      * 
46270      */
46271     cblack: false,
46272     /**
46273      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46274      * 
46275      */
46276     cwhite: false,
46277     
46278      /**
46279      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46280      * 
46281      */
46282     black: false,
46283     /**
46284      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46285      * 
46286      */
46287     white: false,
46288     /**
46289      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46290      */
46291     allowComments: false,
46292     /**
46293      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
46294      */
46295     enableBlocks : true,
46296     
46297     /**
46298      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
46299      *         if you are doing an email editor, this probably needs disabling, it's designed
46300      */
46301     autoClean: true,
46302     /**
46303      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
46304      */
46305     bodyCls : '',
46306     /**
46307      * @cfg {String} language default en - language of text (usefull for rtl languages)
46308      * 
46309      */
46310     language: 'en',
46311     
46312      
46313     // id of frame..
46314     frameId: false,
46315     
46316     // private properties
46317     validationEvent : false,
46318     deferHeight: true,
46319     initialized : false,
46320     activated : false,
46321     
46322     onFocus : Roo.emptyFn,
46323     iframePad:3,
46324     hideMode:'offsets',
46325     
46326     actionMode : 'container', // defaults to hiding it...
46327     
46328     defaultAutoCreate : { // modified by initCompnoent..
46329         tag: "textarea",
46330         style:"width:500px;height:300px;",
46331         autocomplete: "new-password"
46332     },
46333
46334     // private
46335     initComponent : function(){
46336         this.addEvents({
46337             /**
46338              * @event initialize
46339              * Fires when the editor is fully initialized (including the iframe)
46340              * @param {HtmlEditor} this
46341              */
46342             initialize: true,
46343             /**
46344              * @event activate
46345              * Fires when the editor is first receives the focus. Any insertion must wait
46346              * until after this event.
46347              * @param {HtmlEditor} this
46348              */
46349             activate: true,
46350              /**
46351              * @event beforesync
46352              * Fires before the textarea is updated with content from the editor iframe. Return false
46353              * to cancel the sync.
46354              * @param {HtmlEditor} this
46355              * @param {String} html
46356              */
46357             beforesync: true,
46358              /**
46359              * @event beforepush
46360              * Fires before the iframe editor is updated with content from the textarea. Return false
46361              * to cancel the push.
46362              * @param {HtmlEditor} this
46363              * @param {String} html
46364              */
46365             beforepush: true,
46366              /**
46367              * @event sync
46368              * Fires when the textarea is updated with content from the editor iframe.
46369              * @param {HtmlEditor} this
46370              * @param {String} html
46371              */
46372             sync: true,
46373              /**
46374              * @event push
46375              * Fires when the iframe editor is updated with content from the textarea.
46376              * @param {HtmlEditor} this
46377              * @param {String} html
46378              */
46379             push: true,
46380              /**
46381              * @event editmodechange
46382              * Fires when the editor switches edit modes
46383              * @param {HtmlEditor} this
46384              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46385              */
46386             editmodechange: true,
46387             /**
46388              * @event editorevent
46389              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46390              * @param {HtmlEditor} this
46391              */
46392             editorevent: true,
46393             /**
46394              * @event firstfocus
46395              * Fires when on first focus - needed by toolbars..
46396              * @param {HtmlEditor} this
46397              */
46398             firstfocus: true,
46399             /**
46400              * @event autosave
46401              * Auto save the htmlEditor value as a file into Events
46402              * @param {HtmlEditor} this
46403              */
46404             autosave: true,
46405             /**
46406              * @event savedpreview
46407              * preview the saved version of htmlEditor
46408              * @param {HtmlEditor} this
46409              */
46410             savedpreview: true,
46411             
46412             /**
46413             * @event stylesheetsclick
46414             * Fires when press the Sytlesheets button
46415             * @param {Roo.HtmlEditorCore} this
46416             */
46417             stylesheetsclick: true,
46418             /**
46419             * @event paste
46420             * Fires when press user pastes into the editor
46421             * @param {Roo.HtmlEditorCore} this
46422             */
46423             paste: true 
46424         });
46425         this.defaultAutoCreate =  {
46426             tag: "textarea",
46427             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46428             autocomplete: "new-password"
46429         };
46430     },
46431
46432     /**
46433      * Protected method that will not generally be called directly. It
46434      * is called when the editor creates its toolbar. Override this method if you need to
46435      * add custom toolbar buttons.
46436      * @param {HtmlEditor} editor
46437      */
46438     createToolbar : function(editor){
46439         Roo.log("create toolbars");
46440         if (!editor.toolbars || !editor.toolbars.length) {
46441             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46442         }
46443         
46444         for (var i =0 ; i < editor.toolbars.length;i++) {
46445             editor.toolbars[i] = Roo.factory(
46446                     typeof(editor.toolbars[i]) == 'string' ?
46447                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46448                 Roo.form.HtmlEditor);
46449             editor.toolbars[i].init(editor);
46450         }
46451          
46452         
46453     },
46454     /**
46455      * get the Context selected node
46456      * @returns {DomElement|boolean} selected node if active or false if none
46457      * 
46458      */
46459     getSelectedNode : function()
46460     {
46461         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
46462             return false;
46463         }
46464         return this.toolbars[1].tb.selectedNode;
46465     
46466     },
46467     // private
46468     onRender : function(ct, position)
46469     {
46470         var _t = this;
46471         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46472         
46473         this.wrap = this.el.wrap({
46474             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46475         });
46476         
46477         this.editorcore.onRender(ct, position);
46478          
46479         if (this.resizable) {
46480             this.resizeEl = new Roo.Resizable(this.wrap, {
46481                 pinned : true,
46482                 wrap: true,
46483                 dynamic : true,
46484                 minHeight : this.height,
46485                 height: this.height,
46486                 handles : this.resizable,
46487                 width: this.width,
46488                 listeners : {
46489                     resize : function(r, w, h) {
46490                         _t.onResize(w,h); // -something
46491                     }
46492                 }
46493             });
46494             
46495         }
46496         this.createToolbar(this);
46497        
46498         
46499         if(!this.width){
46500             this.setSize(this.wrap.getSize());
46501         }
46502         if (this.resizeEl) {
46503             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46504             // should trigger onReize..
46505         }
46506         
46507         this.keyNav = new Roo.KeyNav(this.el, {
46508             
46509             "tab" : function(e){
46510                 e.preventDefault();
46511                 
46512                 var value = this.getValue();
46513                 
46514                 var start = this.el.dom.selectionStart;
46515                 var end = this.el.dom.selectionEnd;
46516                 
46517                 if(!e.shiftKey){
46518                     
46519                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46520                     this.el.dom.setSelectionRange(end + 1, end + 1);
46521                     return;
46522                 }
46523                 
46524                 var f = value.substring(0, start).split("\t");
46525                 
46526                 if(f.pop().length != 0){
46527                     return;
46528                 }
46529                 
46530                 this.setValue(f.join("\t") + value.substring(end));
46531                 this.el.dom.setSelectionRange(start - 1, start - 1);
46532                 
46533             },
46534             
46535             "home" : function(e){
46536                 e.preventDefault();
46537                 
46538                 var curr = this.el.dom.selectionStart;
46539                 var lines = this.getValue().split("\n");
46540                 
46541                 if(!lines.length){
46542                     return;
46543                 }
46544                 
46545                 if(e.ctrlKey){
46546                     this.el.dom.setSelectionRange(0, 0);
46547                     return;
46548                 }
46549                 
46550                 var pos = 0;
46551                 
46552                 for (var i = 0; i < lines.length;i++) {
46553                     pos += lines[i].length;
46554                     
46555                     if(i != 0){
46556                         pos += 1;
46557                     }
46558                     
46559                     if(pos < curr){
46560                         continue;
46561                     }
46562                     
46563                     pos -= lines[i].length;
46564                     
46565                     break;
46566                 }
46567                 
46568                 if(!e.shiftKey){
46569                     this.el.dom.setSelectionRange(pos, pos);
46570                     return;
46571                 }
46572                 
46573                 this.el.dom.selectionStart = pos;
46574                 this.el.dom.selectionEnd = curr;
46575             },
46576             
46577             "end" : function(e){
46578                 e.preventDefault();
46579                 
46580                 var curr = this.el.dom.selectionStart;
46581                 var lines = this.getValue().split("\n");
46582                 
46583                 if(!lines.length){
46584                     return;
46585                 }
46586                 
46587                 if(e.ctrlKey){
46588                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
46589                     return;
46590                 }
46591                 
46592                 var pos = 0;
46593                 
46594                 for (var i = 0; i < lines.length;i++) {
46595                     
46596                     pos += lines[i].length;
46597                     
46598                     if(i != 0){
46599                         pos += 1;
46600                     }
46601                     
46602                     if(pos < curr){
46603                         continue;
46604                     }
46605                     
46606                     break;
46607                 }
46608                 
46609                 if(!e.shiftKey){
46610                     this.el.dom.setSelectionRange(pos, pos);
46611                     return;
46612                 }
46613                 
46614                 this.el.dom.selectionStart = curr;
46615                 this.el.dom.selectionEnd = pos;
46616             },
46617
46618             scope : this,
46619
46620             doRelay : function(foo, bar, hname){
46621                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46622             },
46623
46624             forceKeyDown: true
46625         });
46626         
46627 //        if(this.autosave && this.w){
46628 //            this.autoSaveFn = setInterval(this.autosave, 1000);
46629 //        }
46630     },
46631
46632     // private
46633     onResize : function(w, h)
46634     {
46635         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
46636         var ew = false;
46637         var eh = false;
46638         
46639         if(this.el ){
46640             if(typeof w == 'number'){
46641                 var aw = w - this.wrap.getFrameWidth('lr');
46642                 this.el.setWidth(this.adjustWidth('textarea', aw));
46643                 ew = aw;
46644             }
46645             if(typeof h == 'number'){
46646                 var tbh = 0;
46647                 for (var i =0; i < this.toolbars.length;i++) {
46648                     // fixme - ask toolbars for heights?
46649                     tbh += this.toolbars[i].tb.el.getHeight();
46650                     if (this.toolbars[i].footer) {
46651                         tbh += this.toolbars[i].footer.el.getHeight();
46652                     }
46653                 }
46654                 
46655                 
46656                 
46657                 
46658                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
46659                 ah -= 5; // knock a few pixes off for look..
46660 //                Roo.log(ah);
46661                 this.el.setHeight(this.adjustWidth('textarea', ah));
46662                 var eh = ah;
46663             }
46664         }
46665         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
46666         this.editorcore.onResize(ew,eh);
46667         
46668     },
46669
46670     /**
46671      * Toggles the editor between standard and source edit mode.
46672      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46673      */
46674     toggleSourceEdit : function(sourceEditMode)
46675     {
46676         this.editorcore.toggleSourceEdit(sourceEditMode);
46677         
46678         if(this.editorcore.sourceEditMode){
46679             Roo.log('editor - showing textarea');
46680             
46681 //            Roo.log('in');
46682 //            Roo.log(this.syncValue());
46683             this.editorcore.syncValue();
46684             this.el.removeClass('x-hidden');
46685             this.el.dom.removeAttribute('tabIndex');
46686             this.el.focus();
46687             this.el.dom.scrollTop = 0;
46688             
46689             
46690             for (var i = 0; i < this.toolbars.length; i++) {
46691                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46692                     this.toolbars[i].tb.hide();
46693                     this.toolbars[i].footer.hide();
46694                 }
46695             }
46696             
46697         }else{
46698             Roo.log('editor - hiding textarea');
46699 //            Roo.log('out')
46700 //            Roo.log(this.pushValue()); 
46701             this.editorcore.pushValue();
46702             
46703             this.el.addClass('x-hidden');
46704             this.el.dom.setAttribute('tabIndex', -1);
46705             
46706             for (var i = 0; i < this.toolbars.length; i++) {
46707                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
46708                     this.toolbars[i].tb.show();
46709                     this.toolbars[i].footer.show();
46710                 }
46711             }
46712             
46713             //this.deferFocus();
46714         }
46715         
46716         this.setSize(this.wrap.getSize());
46717         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
46718         
46719         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
46720     },
46721  
46722     // private (for BoxComponent)
46723     adjustSize : Roo.BoxComponent.prototype.adjustSize,
46724
46725     // private (for BoxComponent)
46726     getResizeEl : function(){
46727         return this.wrap;
46728     },
46729
46730     // private (for BoxComponent)
46731     getPositionEl : function(){
46732         return this.wrap;
46733     },
46734
46735     // private
46736     initEvents : function(){
46737         this.originalValue = this.getValue();
46738     },
46739
46740     /**
46741      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46742      * @method
46743      */
46744     markInvalid : Roo.emptyFn,
46745     /**
46746      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
46747      * @method
46748      */
46749     clearInvalid : Roo.emptyFn,
46750
46751     setValue : function(v){
46752         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
46753         this.editorcore.pushValue();
46754     },
46755
46756     /**
46757      * update the language in the body - really done by core
46758      * @param {String} language - eg. en / ar / zh-CN etc..
46759      */
46760     updateLanguage : function(lang)
46761     {
46762         this.language = lang;
46763         this.editorcore.language = lang;
46764         this.editorcore.updateLanguage();
46765      
46766     },
46767     // private
46768     deferFocus : function(){
46769         this.focus.defer(10, this);
46770     },
46771
46772     // doc'ed in Field
46773     focus : function(){
46774         this.editorcore.focus();
46775         
46776     },
46777       
46778
46779     // private
46780     onDestroy : function(){
46781         
46782         
46783         
46784         if(this.rendered){
46785             
46786             for (var i =0; i < this.toolbars.length;i++) {
46787                 // fixme - ask toolbars for heights?
46788                 this.toolbars[i].onDestroy();
46789             }
46790             
46791             this.wrap.dom.innerHTML = '';
46792             this.wrap.remove();
46793         }
46794     },
46795
46796     // private
46797     onFirstFocus : function(){
46798         //Roo.log("onFirstFocus");
46799         this.editorcore.onFirstFocus();
46800          for (var i =0; i < this.toolbars.length;i++) {
46801             this.toolbars[i].onFirstFocus();
46802         }
46803         
46804     },
46805     
46806     // private
46807     syncValue : function()
46808     {
46809         this.editorcore.syncValue();
46810     },
46811     
46812     pushValue : function()
46813     {
46814         this.editorcore.pushValue();
46815     },
46816     
46817     setStylesheets : function(stylesheets)
46818     {
46819         this.editorcore.setStylesheets(stylesheets);
46820     },
46821     
46822     removeStylesheets : function()
46823     {
46824         this.editorcore.removeStylesheets();
46825     }
46826      
46827     
46828     // hide stuff that is not compatible
46829     /**
46830      * @event blur
46831      * @hide
46832      */
46833     /**
46834      * @event change
46835      * @hide
46836      */
46837     /**
46838      * @event focus
46839      * @hide
46840      */
46841     /**
46842      * @event specialkey
46843      * @hide
46844      */
46845     /**
46846      * @cfg {String} fieldClass @hide
46847      */
46848     /**
46849      * @cfg {String} focusClass @hide
46850      */
46851     /**
46852      * @cfg {String} autoCreate @hide
46853      */
46854     /**
46855      * @cfg {String} inputType @hide
46856      */
46857     /**
46858      * @cfg {String} invalidClass @hide
46859      */
46860     /**
46861      * @cfg {String} invalidText @hide
46862      */
46863     /**
46864      * @cfg {String} msgFx @hide
46865      */
46866     /**
46867      * @cfg {String} validateOnBlur @hide
46868      */
46869 });
46870  
46871     /*
46872  * Based on
46873  * Ext JS Library 1.1.1
46874  * Copyright(c) 2006-2007, Ext JS, LLC.
46875  *  
46876  
46877  */
46878
46879 /**
46880  * @class Roo.form.HtmlEditor.ToolbarStandard
46881  * Basic Toolbar
46882
46883  * Usage:
46884  *
46885  new Roo.form.HtmlEditor({
46886     ....
46887     toolbars : [
46888         new Roo.form.HtmlEditorToolbar1({
46889             disable : { fonts: 1 , format: 1, ..., ... , ...],
46890             btns : [ .... ]
46891         })
46892     }
46893      
46894  * 
46895  * @cfg {Object} disable List of elements to disable..
46896  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
46897  * 
46898  * 
46899  * NEEDS Extra CSS? 
46900  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46901  */
46902  
46903 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46904 {
46905     
46906     Roo.apply(this, config);
46907     
46908     // default disabled, based on 'good practice'..
46909     this.disable = this.disable || {};
46910     Roo.applyIf(this.disable, {
46911         fontSize : true,
46912         colors : true,
46913         specialElements : true
46914     });
46915     
46916     
46917     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46918     // dont call parent... till later.
46919 }
46920
46921 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
46922     
46923     tb: false,
46924     
46925     rendered: false,
46926     
46927     editor : false,
46928     editorcore : false,
46929     /**
46930      * @cfg {Object} disable  List of toolbar elements to disable
46931          
46932      */
46933     disable : false,
46934     
46935     
46936      /**
46937      * @cfg {String} createLinkText The default text for the create link prompt
46938      */
46939     createLinkText : 'Please enter the URL for the link:',
46940     /**
46941      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46942      */
46943     defaultLinkValue : 'http:/'+'/',
46944    
46945     
46946       /**
46947      * @cfg {Array} fontFamilies An array of available font families
46948      */
46949     fontFamilies : [
46950         'Arial',
46951         'Courier New',
46952         'Tahoma',
46953         'Times New Roman',
46954         'Verdana'
46955     ],
46956     
46957     specialChars : [
46958            "&#169;",
46959           "&#174;",     
46960           "&#8482;",    
46961           "&#163;" ,    
46962          // "&#8212;",    
46963           "&#8230;",    
46964           "&#247;" ,    
46965         //  "&#225;" ,     ?? a acute?
46966            "&#8364;"    , //Euro
46967        //   "&#8220;"    ,
46968         //  "&#8221;"    ,
46969         //  "&#8226;"    ,
46970           "&#176;"  //   , // degrees
46971
46972          // "&#233;"     , // e ecute
46973          // "&#250;"     , // u ecute?
46974     ],
46975     
46976     specialElements : [
46977         {
46978             text: "Insert Table",
46979             xtype: 'MenuItem',
46980             xns : Roo.Menu,
46981             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46982                 
46983         },
46984         {    
46985             text: "Insert Image",
46986             xtype: 'MenuItem',
46987             xns : Roo.Menu,
46988             ihtml : '<img src="about:blank"/>'
46989             
46990         }
46991         
46992          
46993     ],
46994     
46995     
46996     inputElements : [ 
46997             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46998             "input:submit", "input:button", "select", "textarea", "label" ],
46999     formats : [
47000         ["p"] ,  
47001         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47002         ["pre"],[ "code"], 
47003         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47004         ['div'],['span'],
47005         ['sup'],['sub']
47006     ],
47007     
47008     cleanStyles : [
47009         "font-size"
47010     ],
47011      /**
47012      * @cfg {String} defaultFont default font to use.
47013      */
47014     defaultFont: 'tahoma',
47015    
47016     fontSelect : false,
47017     
47018     
47019     formatCombo : false,
47020     
47021     init : function(editor)
47022     {
47023         this.editor = editor;
47024         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47025         var editorcore = this.editorcore;
47026         
47027         var _t = this;
47028         
47029         var fid = editorcore.frameId;
47030         var etb = this;
47031         function btn(id, toggle, handler){
47032             var xid = fid + '-'+ id ;
47033             return {
47034                 id : xid,
47035                 cmd : id,
47036                 cls : 'x-btn-icon x-edit-'+id,
47037                 enableToggle:toggle !== false,
47038                 scope: _t, // was editor...
47039                 handler:handler||_t.relayBtnCmd,
47040                 clickEvent:'mousedown',
47041                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47042                 tabIndex:-1
47043             };
47044         }
47045         
47046         
47047         
47048         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47049         this.tb = tb;
47050          // stop form submits
47051         tb.el.on('click', function(e){
47052             e.preventDefault(); // what does this do?
47053         });
47054
47055         if(!this.disable.font) { // && !Roo.isSafari){
47056             /* why no safari for fonts 
47057             editor.fontSelect = tb.el.createChild({
47058                 tag:'select',
47059                 tabIndex: -1,
47060                 cls:'x-font-select',
47061                 html: this.createFontOptions()
47062             });
47063             
47064             editor.fontSelect.on('change', function(){
47065                 var font = editor.fontSelect.dom.value;
47066                 editor.relayCmd('fontname', font);
47067                 editor.deferFocus();
47068             }, editor);
47069             
47070             tb.add(
47071                 editor.fontSelect.dom,
47072                 '-'
47073             );
47074             */
47075             
47076         };
47077         if(!this.disable.formats){
47078             this.formatCombo = new Roo.form.ComboBox({
47079                 store: new Roo.data.SimpleStore({
47080                     id : 'tag',
47081                     fields: ['tag'],
47082                     data : this.formats // from states.js
47083                 }),
47084                 blockFocus : true,
47085                 name : '',
47086                 //autoCreate : {tag: "div",  size: "20"},
47087                 displayField:'tag',
47088                 typeAhead: false,
47089                 mode: 'local',
47090                 editable : false,
47091                 triggerAction: 'all',
47092                 emptyText:'Add tag',
47093                 selectOnFocus:true,
47094                 width:135,
47095                 listeners : {
47096                     'select': function(c, r, i) {
47097                         editorcore.insertTag(r.get('tag'));
47098                         editor.focus();
47099                     }
47100                 }
47101
47102             });
47103             tb.addField(this.formatCombo);
47104             
47105         }
47106         
47107         if(!this.disable.format){
47108             tb.add(
47109                 btn('bold'),
47110                 btn('italic'),
47111                 btn('underline'),
47112                 btn('strikethrough')
47113             );
47114         };
47115         if(!this.disable.fontSize){
47116             tb.add(
47117                 '-',
47118                 
47119                 
47120                 btn('increasefontsize', false, editorcore.adjustFont),
47121                 btn('decreasefontsize', false, editorcore.adjustFont)
47122             );
47123         };
47124         
47125         
47126         if(!this.disable.colors){
47127             tb.add(
47128                 '-', {
47129                     id:editorcore.frameId +'-forecolor',
47130                     cls:'x-btn-icon x-edit-forecolor',
47131                     clickEvent:'mousedown',
47132                     tooltip: this.buttonTips['forecolor'] || undefined,
47133                     tabIndex:-1,
47134                     menu : new Roo.menu.ColorMenu({
47135                         allowReselect: true,
47136                         focus: Roo.emptyFn,
47137                         value:'000000',
47138                         plain:true,
47139                         selectHandler: function(cp, color){
47140                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47141                             editor.deferFocus();
47142                         },
47143                         scope: editorcore,
47144                         clickEvent:'mousedown'
47145                     })
47146                 }, {
47147                     id:editorcore.frameId +'backcolor',
47148                     cls:'x-btn-icon x-edit-backcolor',
47149                     clickEvent:'mousedown',
47150                     tooltip: this.buttonTips['backcolor'] || undefined,
47151                     tabIndex:-1,
47152                     menu : new Roo.menu.ColorMenu({
47153                         focus: Roo.emptyFn,
47154                         value:'FFFFFF',
47155                         plain:true,
47156                         allowReselect: true,
47157                         selectHandler: function(cp, color){
47158                             if(Roo.isGecko){
47159                                 editorcore.execCmd('useCSS', false);
47160                                 editorcore.execCmd('hilitecolor', color);
47161                                 editorcore.execCmd('useCSS', true);
47162                                 editor.deferFocus();
47163                             }else{
47164                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47165                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47166                                 editor.deferFocus();
47167                             }
47168                         },
47169                         scope:editorcore,
47170                         clickEvent:'mousedown'
47171                     })
47172                 }
47173             );
47174         };
47175         // now add all the items...
47176         
47177
47178         if(!this.disable.alignments){
47179             tb.add(
47180                 '-',
47181                 btn('justifyleft'),
47182                 btn('justifycenter'),
47183                 btn('justifyright')
47184             );
47185         };
47186
47187         //if(!Roo.isSafari){
47188             if(!this.disable.links){
47189                 tb.add(
47190                     '-',
47191                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47192                 );
47193             };
47194
47195             if(!this.disable.lists){
47196                 tb.add(
47197                     '-',
47198                     btn('insertorderedlist'),
47199                     btn('insertunorderedlist')
47200                 );
47201             }
47202             if(!this.disable.sourceEdit){
47203                 tb.add(
47204                     '-',
47205                     btn('sourceedit', true, function(btn){
47206                         this.toggleSourceEdit(btn.pressed);
47207                     })
47208                 );
47209             }
47210         //}
47211         
47212         var smenu = { };
47213         // special menu.. - needs to be tidied up..
47214         if (!this.disable.special) {
47215             smenu = {
47216                 text: "&#169;",
47217                 cls: 'x-edit-none',
47218                 
47219                 menu : {
47220                     items : []
47221                 }
47222             };
47223             for (var i =0; i < this.specialChars.length; i++) {
47224                 smenu.menu.items.push({
47225                     
47226                     html: this.specialChars[i],
47227                     handler: function(a,b) {
47228                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47229                         //editor.insertAtCursor(a.html);
47230                         
47231                     },
47232                     tabIndex:-1
47233                 });
47234             }
47235             
47236             
47237             tb.add(smenu);
47238             
47239             
47240         }
47241         
47242         var cmenu = { };
47243         if (!this.disable.cleanStyles) {
47244             cmenu = {
47245                 cls: 'x-btn-icon x-btn-clear',
47246                 
47247                 menu : {
47248                     items : []
47249                 }
47250             };
47251             for (var i =0; i < this.cleanStyles.length; i++) {
47252                 cmenu.menu.items.push({
47253                     actiontype : this.cleanStyles[i],
47254                     html: 'Remove ' + this.cleanStyles[i],
47255                     handler: function(a,b) {
47256 //                        Roo.log(a);
47257 //                        Roo.log(b);
47258                         var c = Roo.get(editorcore.doc.body);
47259                         c.select('[style]').each(function(s) {
47260                             s.dom.style.removeProperty(a.actiontype);
47261                         });
47262                         editorcore.syncValue();
47263                     },
47264                     tabIndex:-1
47265                 });
47266             }
47267             cmenu.menu.items.push({
47268                 actiontype : 'tablewidths',
47269                 html: 'Remove Table Widths',
47270                 handler: function(a,b) {
47271                     editorcore.cleanTableWidths();
47272                     editorcore.syncValue();
47273                 },
47274                 tabIndex:-1
47275             });
47276             cmenu.menu.items.push({
47277                 actiontype : 'word',
47278                 html: 'Remove MS Word Formating',
47279                 handler: function(a,b) {
47280                     editorcore.cleanWord();
47281                     editorcore.syncValue();
47282                 },
47283                 tabIndex:-1
47284             });
47285             
47286             cmenu.menu.items.push({
47287                 actiontype : 'all',
47288                 html: 'Remove All Styles',
47289                 handler: function(a,b) {
47290                     
47291                     var c = Roo.get(editorcore.doc.body);
47292                     c.select('[style]').each(function(s) {
47293                         s.dom.removeAttribute('style');
47294                     });
47295                     editorcore.syncValue();
47296                 },
47297                 tabIndex:-1
47298             });
47299             
47300             cmenu.menu.items.push({
47301                 actiontype : 'all',
47302                 html: 'Remove All CSS Classes',
47303                 handler: function(a,b) {
47304                     
47305                     var c = Roo.get(editorcore.doc.body);
47306                     c.select('[class]').each(function(s) {
47307                         s.dom.removeAttribute('class');
47308                     });
47309                     editorcore.cleanWord();
47310                     editorcore.syncValue();
47311                 },
47312                 tabIndex:-1
47313             });
47314             
47315              cmenu.menu.items.push({
47316                 actiontype : 'tidy',
47317                 html: 'Tidy HTML Source',
47318                 handler: function(a,b) {
47319                     new Roo.htmleditor.Tidy(editorcore.doc.body);
47320                     editorcore.syncValue();
47321                 },
47322                 tabIndex:-1
47323             });
47324             
47325             
47326             tb.add(cmenu);
47327         }
47328          
47329         if (!this.disable.specialElements) {
47330             var semenu = {
47331                 text: "Other;",
47332                 cls: 'x-edit-none',
47333                 menu : {
47334                     items : []
47335                 }
47336             };
47337             for (var i =0; i < this.specialElements.length; i++) {
47338                 semenu.menu.items.push(
47339                     Roo.apply({ 
47340                         handler: function(a,b) {
47341                             editor.insertAtCursor(this.ihtml);
47342                         }
47343                     }, this.specialElements[i])
47344                 );
47345                     
47346             }
47347             
47348             tb.add(semenu);
47349             
47350             
47351         }
47352          
47353         
47354         if (this.btns) {
47355             for(var i =0; i< this.btns.length;i++) {
47356                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
47357                 b.cls =  'x-edit-none';
47358                 
47359                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47360                     b.cls += ' x-init-enable';
47361                 }
47362                 
47363                 b.scope = editorcore;
47364                 tb.add(b);
47365             }
47366         
47367         }
47368         
47369         
47370         
47371         // disable everything...
47372         
47373         this.tb.items.each(function(item){
47374             
47375            if(
47376                 item.id != editorcore.frameId+ '-sourceedit' && 
47377                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47378             ){
47379                 
47380                 item.disable();
47381             }
47382         });
47383         this.rendered = true;
47384         
47385         // the all the btns;
47386         editor.on('editorevent', this.updateToolbar, this);
47387         // other toolbars need to implement this..
47388         //editor.on('editmodechange', this.updateToolbar, this);
47389     },
47390     
47391     
47392     relayBtnCmd : function(btn) {
47393         this.editorcore.relayCmd(btn.cmd);
47394     },
47395     // private used internally
47396     createLink : function(){
47397         //Roo.log("create link?");
47398         var ec = this.editorcore;
47399         var ar = ec.getAllAncestors();
47400         var n = false;
47401         for(var i = 0;i< ar.length;i++) {
47402             if (ar[i] && ar[i].nodeName == 'A') {
47403                 n = ar[i];
47404                 break;
47405             }
47406         }
47407         
47408         (function() {
47409             
47410             Roo.MessageBox.show({
47411                 title : "Add / Edit Link URL",
47412                 msg : "Enter the url for the link",
47413                 buttons: Roo.MessageBox.OKCANCEL,
47414                 fn: function(btn, url){
47415                     if (btn != 'ok') {
47416                         return;
47417                     }
47418                     if(url && url != 'http:/'+'/'){
47419                         if (n) {
47420                             n.setAttribute('href', url);
47421                         } else {
47422                             ec.relayCmd('createlink', url);
47423                         }
47424                     }
47425                 },
47426                 minWidth:250,
47427                 prompt:true,
47428                 //multiline: multiline,
47429                 modal : true,
47430                 value :  n  ? n.getAttribute('href') : '' 
47431             });
47432             
47433              
47434         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
47435         
47436     },
47437
47438     
47439     /**
47440      * Protected method that will not generally be called directly. It triggers
47441      * a toolbar update by reading the markup state of the current selection in the editor.
47442      */
47443     updateToolbar: function(){
47444
47445         if(!this.editorcore.activated){
47446             this.editor.onFirstFocus();
47447             return;
47448         }
47449
47450         var btns = this.tb.items.map, 
47451             doc = this.editorcore.doc,
47452             frameId = this.editorcore.frameId;
47453
47454         if(!this.disable.font && !Roo.isSafari){
47455             /*
47456             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47457             if(name != this.fontSelect.dom.value){
47458                 this.fontSelect.dom.value = name;
47459             }
47460             */
47461         }
47462         if(!this.disable.format){
47463             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47464             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47465             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47466             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47467         }
47468         if(!this.disable.alignments){
47469             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47470             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47471             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47472         }
47473         if(!Roo.isSafari && !this.disable.lists){
47474             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47475             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47476         }
47477         
47478         var ans = this.editorcore.getAllAncestors();
47479         if (this.formatCombo) {
47480             
47481             
47482             var store = this.formatCombo.store;
47483             this.formatCombo.setValue("");
47484             for (var i =0; i < ans.length;i++) {
47485                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47486                     // select it..
47487                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47488                     break;
47489                 }
47490             }
47491         }
47492         
47493         
47494         
47495         // hides menus... - so this cant be on a menu...
47496         Roo.menu.MenuMgr.hideAll();
47497
47498         //this.editorsyncValue();
47499     },
47500    
47501     
47502     createFontOptions : function(){
47503         var buf = [], fs = this.fontFamilies, ff, lc;
47504         
47505         
47506         
47507         for(var i = 0, len = fs.length; i< len; i++){
47508             ff = fs[i];
47509             lc = ff.toLowerCase();
47510             buf.push(
47511                 '<option value="',lc,'" style="font-family:',ff,';"',
47512                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47513                     ff,
47514                 '</option>'
47515             );
47516         }
47517         return buf.join('');
47518     },
47519     
47520     toggleSourceEdit : function(sourceEditMode){
47521         
47522         Roo.log("toolbar toogle");
47523         if(sourceEditMode === undefined){
47524             sourceEditMode = !this.sourceEditMode;
47525         }
47526         this.sourceEditMode = sourceEditMode === true;
47527         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47528         // just toggle the button?
47529         if(btn.pressed !== this.sourceEditMode){
47530             btn.toggle(this.sourceEditMode);
47531             return;
47532         }
47533         
47534         if(sourceEditMode){
47535             Roo.log("disabling buttons");
47536             this.tb.items.each(function(item){
47537                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47538                     item.disable();
47539                 }
47540             });
47541           
47542         }else{
47543             Roo.log("enabling buttons");
47544             if(this.editorcore.initialized){
47545                 this.tb.items.each(function(item){
47546                     item.enable();
47547                 });
47548                 // initialize 'blocks'
47549                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
47550                     Roo.htmleditor.Block.factory(e).updateElement(e);
47551                 },this);
47552             
47553             }
47554             
47555         }
47556         Roo.log("calling toggole on editor");
47557         // tell the editor that it's been pressed..
47558         this.editor.toggleSourceEdit(sourceEditMode);
47559        
47560     },
47561      /**
47562      * Object collection of toolbar tooltips for the buttons in the editor. The key
47563      * is the command id associated with that button and the value is a valid QuickTips object.
47564      * For example:
47565 <pre><code>
47566 {
47567     bold : {
47568         title: 'Bold (Ctrl+B)',
47569         text: 'Make the selected text bold.',
47570         cls: 'x-html-editor-tip'
47571     },
47572     italic : {
47573         title: 'Italic (Ctrl+I)',
47574         text: 'Make the selected text italic.',
47575         cls: 'x-html-editor-tip'
47576     },
47577     ...
47578 </code></pre>
47579     * @type Object
47580      */
47581     buttonTips : {
47582         bold : {
47583             title: 'Bold (Ctrl+B)',
47584             text: 'Make the selected text bold.',
47585             cls: 'x-html-editor-tip'
47586         },
47587         italic : {
47588             title: 'Italic (Ctrl+I)',
47589             text: 'Make the selected text italic.',
47590             cls: 'x-html-editor-tip'
47591         },
47592         underline : {
47593             title: 'Underline (Ctrl+U)',
47594             text: 'Underline the selected text.',
47595             cls: 'x-html-editor-tip'
47596         },
47597         strikethrough : {
47598             title: 'Strikethrough',
47599             text: 'Strikethrough the selected text.',
47600             cls: 'x-html-editor-tip'
47601         },
47602         increasefontsize : {
47603             title: 'Grow Text',
47604             text: 'Increase the font size.',
47605             cls: 'x-html-editor-tip'
47606         },
47607         decreasefontsize : {
47608             title: 'Shrink Text',
47609             text: 'Decrease the font size.',
47610             cls: 'x-html-editor-tip'
47611         },
47612         backcolor : {
47613             title: 'Text Highlight Color',
47614             text: 'Change the background color of the selected text.',
47615             cls: 'x-html-editor-tip'
47616         },
47617         forecolor : {
47618             title: 'Font Color',
47619             text: 'Change the color of the selected text.',
47620             cls: 'x-html-editor-tip'
47621         },
47622         justifyleft : {
47623             title: 'Align Text Left',
47624             text: 'Align text to the left.',
47625             cls: 'x-html-editor-tip'
47626         },
47627         justifycenter : {
47628             title: 'Center Text',
47629             text: 'Center text in the editor.',
47630             cls: 'x-html-editor-tip'
47631         },
47632         justifyright : {
47633             title: 'Align Text Right',
47634             text: 'Align text to the right.',
47635             cls: 'x-html-editor-tip'
47636         },
47637         insertunorderedlist : {
47638             title: 'Bullet List',
47639             text: 'Start a bulleted list.',
47640             cls: 'x-html-editor-tip'
47641         },
47642         insertorderedlist : {
47643             title: 'Numbered List',
47644             text: 'Start a numbered list.',
47645             cls: 'x-html-editor-tip'
47646         },
47647         createlink : {
47648             title: 'Hyperlink',
47649             text: 'Make the selected text a hyperlink.',
47650             cls: 'x-html-editor-tip'
47651         },
47652         sourceedit : {
47653             title: 'Source Edit',
47654             text: 'Switch to source editing mode.',
47655             cls: 'x-html-editor-tip'
47656         }
47657     },
47658     // private
47659     onDestroy : function(){
47660         if(this.rendered){
47661             
47662             this.tb.items.each(function(item){
47663                 if(item.menu){
47664                     item.menu.removeAll();
47665                     if(item.menu.el){
47666                         item.menu.el.destroy();
47667                     }
47668                 }
47669                 item.destroy();
47670             });
47671              
47672         }
47673     },
47674     onFirstFocus: function() {
47675         this.tb.items.each(function(item){
47676            item.enable();
47677         });
47678     }
47679 };
47680
47681
47682
47683
47684 // <script type="text/javascript">
47685 /*
47686  * Based on
47687  * Ext JS Library 1.1.1
47688  * Copyright(c) 2006-2007, Ext JS, LLC.
47689  *  
47690  
47691  */
47692
47693  
47694 /**
47695  * @class Roo.form.HtmlEditor.ToolbarContext
47696  * Context Toolbar
47697  * 
47698  * Usage:
47699  *
47700  new Roo.form.HtmlEditor({
47701     ....
47702     toolbars : [
47703         { xtype: 'ToolbarStandard', styles : {} }
47704         { xtype: 'ToolbarContext', disable : {} }
47705     ]
47706 })
47707
47708      
47709  * 
47710  * @config : {Object} disable List of elements to disable.. (not done yet.)
47711  * @config : {Object} styles  Map of styles available.
47712  * 
47713  */
47714
47715 Roo.form.HtmlEditor.ToolbarContext = function(config)
47716 {
47717     
47718     Roo.apply(this, config);
47719     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47720     // dont call parent... till later.
47721     this.styles = this.styles || {};
47722 }
47723
47724  
47725
47726 Roo.form.HtmlEditor.ToolbarContext.types = {
47727     'IMG' : [
47728         {
47729             name : 'width',
47730             title: "Width",
47731             width: 40
47732         },
47733         {
47734             name : 'height',
47735             title: "Height",
47736             width: 40
47737         },
47738         {
47739             name : 'align',
47740             title: "Align",
47741             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47742             width : 80
47743             
47744         },
47745         {
47746             name : 'border',
47747             title: "Border",
47748             width: 40
47749         },
47750         {
47751             name : 'alt',
47752             title: "Alt",
47753             width: 120
47754         },
47755         {
47756             name : 'src',
47757             title: "Src",
47758             width: 220
47759         }
47760         
47761     ],
47762     
47763     'FIGURE' : [
47764         {
47765             name : 'align',
47766             title: "Align",
47767             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
47768             width : 80  
47769         }
47770     ],
47771     'A' : [
47772         {
47773             name : 'name',
47774             title: "Name",
47775             width: 50
47776         },
47777         {
47778             name : 'target',
47779             title: "Target",
47780             width: 120
47781         },
47782         {
47783             name : 'href',
47784             title: "Href",
47785             width: 220
47786         } // border?
47787         
47788     ],
47789     
47790     'INPUT' : [
47791         {
47792             name : 'name',
47793             title: "name",
47794             width: 120
47795         },
47796         {
47797             name : 'value',
47798             title: "Value",
47799             width: 120
47800         },
47801         {
47802             name : 'width',
47803             title: "Width",
47804             width: 40
47805         }
47806     ],
47807     'LABEL' : [
47808          {
47809             name : 'for',
47810             title: "For",
47811             width: 120
47812         }
47813     ],
47814     'TEXTAREA' : [
47815         {
47816             name : 'name',
47817             title: "name",
47818             width: 120
47819         },
47820         {
47821             name : 'rows',
47822             title: "Rows",
47823             width: 20
47824         },
47825         {
47826             name : 'cols',
47827             title: "Cols",
47828             width: 20
47829         }
47830     ],
47831     'SELECT' : [
47832         {
47833             name : 'name',
47834             title: "name",
47835             width: 120
47836         },
47837         {
47838             name : 'selectoptions',
47839             title: "Options",
47840             width: 200
47841         }
47842     ],
47843     
47844     // should we really allow this??
47845     // should this just be 
47846     'BODY' : [
47847         
47848         {
47849             name : 'title',
47850             title: "Title",
47851             width: 200,
47852             disabled : true
47853         }
47854     ],
47855  
47856     '*' : [
47857         // empty.
47858     ]
47859
47860 };
47861
47862 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47863 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47864
47865 Roo.form.HtmlEditor.ToolbarContext.options = {
47866         'font-family'  : [ 
47867                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47868                 [ 'Courier New', 'Courier New'],
47869                 [ 'Tahoma', 'Tahoma'],
47870                 [ 'Times New Roman,serif', 'Times'],
47871                 [ 'Verdana','Verdana' ]
47872         ]
47873 };
47874
47875 // fixme - these need to be configurable..
47876  
47877
47878 //Roo.form.HtmlEditor.ToolbarContext.types
47879
47880
47881 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47882     
47883     tb: false,
47884     
47885     rendered: false,
47886     
47887     editor : false,
47888     editorcore : false,
47889     /**
47890      * @cfg {Object} disable  List of toolbar elements to disable
47891          
47892      */
47893     disable : false,
47894     /**
47895      * @cfg {Object} styles List of styles 
47896      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47897      *
47898      * These must be defined in the page, so they get rendered correctly..
47899      * .headline { }
47900      * TD.underline { }
47901      * 
47902      */
47903     styles : false,
47904     
47905     options: false,
47906     
47907     toolbars : false,
47908     
47909     init : function(editor)
47910     {
47911         this.editor = editor;
47912         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47913         var editorcore = this.editorcore;
47914         
47915         var fid = editorcore.frameId;
47916         var etb = this;
47917         function btn(id, toggle, handler){
47918             var xid = fid + '-'+ id ;
47919             return {
47920                 id : xid,
47921                 cmd : id,
47922                 cls : 'x-btn-icon x-edit-'+id,
47923                 enableToggle:toggle !== false,
47924                 scope: editorcore, // was editor...
47925                 handler:handler||editorcore.relayBtnCmd,
47926                 clickEvent:'mousedown',
47927                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47928                 tabIndex:-1
47929             };
47930         }
47931         // create a new element.
47932         var wdiv = editor.wrap.createChild({
47933                 tag: 'div'
47934             }, editor.wrap.dom.firstChild.nextSibling, true);
47935         
47936         // can we do this more than once??
47937         
47938          // stop form submits
47939       
47940  
47941         // disable everything...
47942         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47943         this.toolbars = {};
47944         // block toolbars are built in updateToolbar when needed.
47945         for (var i in  ty) {
47946             
47947             this.toolbars[i] = this.buildToolbar(ty[i],i);
47948         }
47949         this.tb = this.toolbars.BODY;
47950         this.tb.el.show();
47951         this.buildFooter();
47952         this.footer.show();
47953         editor.on('hide', function( ) { this.footer.hide() }, this);
47954         editor.on('show', function( ) { this.footer.show() }, this);
47955         
47956          
47957         this.rendered = true;
47958         
47959         // the all the btns;
47960         editor.on('editorevent', this.updateToolbar, this);
47961         // other toolbars need to implement this..
47962         //editor.on('editmodechange', this.updateToolbar, this);
47963     },
47964     
47965     
47966     
47967     /**
47968      * Protected method that will not generally be called directly. It triggers
47969      * a toolbar update by reading the markup state of the current selection in the editor.
47970      *
47971      * Note you can force an update by calling on('editorevent', scope, false)
47972      */
47973     updateToolbar: function(editor ,ev, sel)
47974     {
47975         
47976         if (ev) {
47977             ev.stopEvent(); // se if we can stop this looping with mutiple events.
47978         }
47979         
47980         //Roo.log(ev);
47981         // capture mouse up - this is handy for selecting images..
47982         // perhaps should go somewhere else...
47983         if(!this.editorcore.activated){
47984              this.editor.onFirstFocus();
47985             return;
47986         }
47987         //Roo.log(ev ? ev.target : 'NOTARGET');
47988         
47989         
47990         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47991         // selectNode - might want to handle IE?
47992         
47993         
47994         
47995         if (ev &&
47996             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47997             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
47998             // they have click on an image...
47999             // let's see if we can change the selection...
48000             sel = ev.target;
48001             
48002             // this triggers looping?
48003             //this.editorcore.selectNode(sel);
48004              
48005         }
48006         
48007         // this forces an id..
48008         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
48009              e.classList.remove('roo-ed-selection');
48010         });
48011         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
48012         //Roo.get(node).addClass('roo-ed-selection');
48013       
48014         //var updateFooter = sel ? false : true; 
48015         
48016         
48017         var ans = this.editorcore.getAllAncestors();
48018         
48019         // pick
48020         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
48021         
48022         if (!sel) { 
48023             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48024             sel = sel ? sel : this.editorcore.doc.body;
48025             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48026             
48027         }
48028         
48029         var tn = sel.tagName.toUpperCase();
48030         var lastSel = this.tb.selectedNode;
48031         this.tb.selectedNode = sel;
48032         var left_label = tn;
48033         
48034         // ok see if we are editing a block?
48035         
48036         var db = false;
48037         // you are not actually selecting the block.
48038         if (sel && sel.hasAttribute('data-block')) {
48039             db = sel;
48040         } else if (sel && sel.closest('[data-block]')) {
48041             
48042             db = sel.closest('[data-block]');
48043             //var cepar = sel.closest('[contenteditable=true]');
48044             //if (db && cepar && cepar.tagName != 'BODY') {
48045             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
48046             //}   
48047         }
48048         
48049         
48050         var block = false;
48051         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
48052         if (db && this.editorcore.enableBlocks) {
48053             block = Roo.htmleditor.Block.factory(db);
48054             
48055             
48056             if (block) {
48057                  db.className = (
48058                         db.classList.length > 0  ? db.className + ' ' : ''
48059                     )  + 'roo-ed-selection';
48060                  
48061                  // since we removed it earlier... its not there..
48062                 tn = 'BLOCK.' + db.getAttribute('data-block');
48063                 
48064                 //this.editorcore.selectNode(db);
48065                 if (typeof(this.toolbars[tn]) == 'undefined') {
48066                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
48067                 }
48068                 this.toolbars[tn].selectedNode = db;
48069                 left_label = block.friendly_name;
48070                 ans = this.editorcore.getAllAncestors();
48071             }
48072             
48073                 
48074             
48075         }
48076         
48077         
48078         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
48079             return; // no change?
48080         }
48081         
48082         
48083           
48084         this.tb.el.hide();
48085         ///console.log("show: " + tn);
48086         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48087         
48088         this.tb.el.show();
48089         // update name
48090         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
48091         
48092         
48093         // update attributes
48094         if (block && this.tb.fields) {
48095              
48096             this.tb.fields.each(function(e) {
48097                 e.setValue(block[e.name]);
48098             });
48099             
48100             
48101         } else  if (this.tb.fields && this.tb.selectedNode) {
48102             this.tb.fields.each( function(e) {
48103                 if (e.stylename) {
48104                     e.setValue(this.tb.selectedNode.style[e.stylename]);
48105                     return;
48106                 } 
48107                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
48108             }, this);
48109             this.updateToolbarStyles(this.tb.selectedNode);  
48110         }
48111         
48112         
48113        
48114         Roo.menu.MenuMgr.hideAll();
48115
48116         
48117         
48118     
48119         // update the footer
48120         //
48121         this.updateFooter(ans);
48122              
48123     },
48124     
48125     updateToolbarStyles : function(sel)
48126     {
48127         var hasStyles = false;
48128         for(var i in this.styles) {
48129             hasStyles = true;
48130             break;
48131         }
48132         
48133         // update styles
48134         if (hasStyles && this.tb.hasStyles) { 
48135             var st = this.tb.fields.item(0);
48136             
48137             st.store.removeAll();
48138             var cn = sel.className.split(/\s+/);
48139             
48140             var avs = [];
48141             if (this.styles['*']) {
48142                 
48143                 Roo.each(this.styles['*'], function(v) {
48144                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48145                 });
48146             }
48147             if (this.styles[tn]) { 
48148                 Roo.each(this.styles[tn], function(v) {
48149                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48150                 });
48151             }
48152             
48153             st.store.loadData(avs);
48154             st.collapse();
48155             st.setValue(cn);
48156         }
48157     },
48158     
48159      
48160     updateFooter : function(ans)
48161     {
48162         var html = '';
48163         if (ans === false) {
48164             this.footDisp.dom.innerHTML = '';
48165             return;
48166         }
48167         
48168         this.footerEls = ans.reverse();
48169         Roo.each(this.footerEls, function(a,i) {
48170             if (!a) { return; }
48171             html += html.length ? ' &gt; '  :  '';
48172             
48173             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48174             
48175         });
48176        
48177         // 
48178         var sz = this.footDisp.up('td').getSize();
48179         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48180         this.footDisp.dom.style.marginLeft = '5px';
48181         
48182         this.footDisp.dom.style.overflow = 'hidden';
48183         
48184         this.footDisp.dom.innerHTML = html;
48185             
48186         
48187     },
48188    
48189        
48190     // private
48191     onDestroy : function(){
48192         if(this.rendered){
48193             
48194             this.tb.items.each(function(item){
48195                 if(item.menu){
48196                     item.menu.removeAll();
48197                     if(item.menu.el){
48198                         item.menu.el.destroy();
48199                     }
48200                 }
48201                 item.destroy();
48202             });
48203              
48204         }
48205     },
48206     onFirstFocus: function() {
48207         // need to do this for all the toolbars..
48208         this.tb.items.each(function(item){
48209            item.enable();
48210         });
48211     },
48212     buildToolbar: function(tlist, nm, friendly_name, block)
48213     {
48214         var editor = this.editor;
48215         var editorcore = this.editorcore;
48216          // create a new element.
48217         var wdiv = editor.wrap.createChild({
48218                 tag: 'div'
48219             }, editor.wrap.dom.firstChild.nextSibling, true);
48220         
48221        
48222         var tb = new Roo.Toolbar(wdiv);
48223         ///this.tb = tb; // << this sets the active toolbar..
48224         if (tlist === false && block) {
48225             tlist = block.contextMenu(this);
48226         }
48227         
48228         tb.hasStyles = false;
48229         tb.name = nm;
48230         
48231         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
48232         
48233         var styles = Array.from(this.styles);
48234         
48235         
48236         // styles...
48237         if (styles && styles.length) {
48238             tb.hasStyles = true;
48239             // this needs a multi-select checkbox...
48240             tb.addField( new Roo.form.ComboBox({
48241                 store: new Roo.data.SimpleStore({
48242                     id : 'val',
48243                     fields: ['val', 'selected'],
48244                     data : [] 
48245                 }),
48246                 name : '-roo-edit-className',
48247                 attrname : 'className',
48248                 displayField: 'val',
48249                 typeAhead: false,
48250                 mode: 'local',
48251                 editable : false,
48252                 triggerAction: 'all',
48253                 emptyText:'Select Style',
48254                 selectOnFocus:true,
48255                 width: 130,
48256                 listeners : {
48257                     'select': function(c, r, i) {
48258                         // initial support only for on class per el..
48259                         tb.selectedNode.className =  r ? r.get('val') : '';
48260                         editorcore.syncValue();
48261                     }
48262                 }
48263     
48264             }));
48265         }
48266         
48267         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48268         
48269         
48270         for (var i = 0; i < tlist.length; i++) {
48271             
48272             // newer versions will use xtype cfg to create menus.
48273             if (typeof(tlist[i].xtype) != 'undefined') {
48274                 
48275                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
48276                 
48277                 
48278                 continue;
48279             }
48280             
48281             var item = tlist[i];
48282             tb.add(item.title + ":&nbsp;");
48283             
48284             
48285             //optname == used so you can configure the options available..
48286             var opts = item.opts ? item.opts : false;
48287             if (item.optname) { // use the b
48288                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
48289            
48290             }
48291             
48292             if (opts) {
48293                 // opts == pulldown..
48294                 tb.addField( new Roo.form.ComboBox({
48295                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48296                         id : 'val',
48297                         fields: ['val', 'display'],
48298                         data : opts  
48299                     }),
48300                     name : '-roo-edit-' + tlist[i].name,
48301                     
48302                     attrname : tlist[i].name,
48303                     stylename : item.style ? item.style : false,
48304                     
48305                     displayField: item.displayField ? item.displayField : 'val',
48306                     valueField :  'val',
48307                     typeAhead: false,
48308                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
48309                     editable : false,
48310                     triggerAction: 'all',
48311                     emptyText:'Select',
48312                     selectOnFocus:true,
48313                     width: item.width ? item.width  : 130,
48314                     listeners : {
48315                         'select': function(c, r, i) {
48316                              
48317                             
48318                             if (c.stylename) {
48319                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48320                                 editorcore.syncValue();
48321                                 return;
48322                             }
48323                             if (r === false) {
48324                                 tb.selectedNode.removeAttribute(c.attrname);
48325                                 editorcore.syncValue();
48326                                 return;
48327                             }
48328                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48329                             editorcore.syncValue();
48330                         }
48331                     }
48332
48333                 }));
48334                 continue;
48335                     
48336                  
48337                 /*
48338                 tb.addField( new Roo.form.TextField({
48339                     name: i,
48340                     width: 100,
48341                     //allowBlank:false,
48342                     value: ''
48343                 }));
48344                 continue;
48345                 */
48346             }
48347             tb.addField( new Roo.form.TextField({
48348                 name: '-roo-edit-' + tlist[i].name,
48349                 attrname : tlist[i].name,
48350                 
48351                 width: item.width,
48352                 //allowBlank:true,
48353                 value: '',
48354                 listeners: {
48355                     'change' : function(f, nv, ov) {
48356                         
48357                          
48358                         tb.selectedNode.setAttribute(f.attrname, nv);
48359                         editorcore.syncValue();
48360                     }
48361                 }
48362             }));
48363              
48364         }
48365         
48366         var _this = this;
48367         var show_delete = !block || block.deleteTitle !== false;
48368         if(nm == 'BODY'){
48369             show_delete = false;
48370             tb.addSeparator();
48371         
48372             tb.addButton( {
48373                 text: 'Stylesheets',
48374
48375                 listeners : {
48376                     click : function ()
48377                     {
48378                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48379                     }
48380                 }
48381             });
48382         }
48383         
48384         tb.addFill();
48385         if (show_delete) {
48386             tb.addButton({
48387                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
48388         
48389                 listeners : {
48390                     click : function ()
48391                     {
48392                         var sn = tb.selectedNode;
48393                         if (block) {
48394                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
48395                             
48396                         }
48397                         if (!sn) {
48398                             return;
48399                         }
48400                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
48401                         if (sn.hasAttribute('data-block')) {
48402                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
48403                             sn.parentNode.removeChild(sn);
48404                             
48405                         } else if (sn && sn.tagName != 'BODY') {
48406                             // remove and keep parents.
48407                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
48408                             a.replaceTag(sn);
48409                         }
48410                         
48411                         
48412                         var range = editorcore.createRange();
48413             
48414                         range.setStart(stn,0);
48415                         range.setEnd(stn,0); 
48416                         var selection = editorcore.getSelection();
48417                         selection.removeAllRanges();
48418                         selection.addRange(range);
48419                         
48420                         
48421                         //_this.updateToolbar(null, null, pn);
48422                         _this.updateToolbar(null, null, null);
48423                         _this.updateFooter(false);
48424                         
48425                     }
48426                 }
48427                 
48428                         
48429                     
48430                 
48431             });
48432         }    
48433         
48434         tb.el.on('click', function(e){
48435             e.preventDefault(); // what does this do?
48436         });
48437         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48438         tb.el.hide();
48439         
48440         // dont need to disable them... as they will get hidden
48441         return tb;
48442          
48443         
48444     },
48445     buildFooter : function()
48446     {
48447         
48448         var fel = this.editor.wrap.createChild();
48449         this.footer = new Roo.Toolbar(fel);
48450         // toolbar has scrolly on left / right?
48451         var footDisp= new Roo.Toolbar.Fill();
48452         var _t = this;
48453         this.footer.add(
48454             {
48455                 text : '&lt;',
48456                 xtype: 'Button',
48457                 handler : function() {
48458                     _t.footDisp.scrollTo('left',0,true)
48459                 }
48460             }
48461         );
48462         this.footer.add( footDisp );
48463         this.footer.add( 
48464             {
48465                 text : '&gt;',
48466                 xtype: 'Button',
48467                 handler : function() {
48468                     // no animation..
48469                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48470                 }
48471             }
48472         );
48473         var fel = Roo.get(footDisp.el);
48474         fel.addClass('x-editor-context');
48475         this.footDispWrap = fel; 
48476         this.footDispWrap.overflow  = 'hidden';
48477         
48478         this.footDisp = fel.createChild();
48479         this.footDispWrap.on('click', this.onContextClick, this)
48480         
48481         
48482     },
48483     // when the footer contect changes
48484     onContextClick : function (ev,dom)
48485     {
48486         ev.preventDefault();
48487         var  cn = dom.className;
48488         //Roo.log(cn);
48489         if (!cn.match(/x-ed-loc-/)) {
48490             return;
48491         }
48492         var n = cn.split('-').pop();
48493         var ans = this.footerEls;
48494         var sel = ans[n];
48495         
48496         this.editorcore.selectNode(sel);
48497         
48498         
48499         this.updateToolbar(null, null, sel);
48500         
48501         
48502     }
48503     
48504     
48505     
48506     
48507     
48508 });
48509
48510
48511
48512
48513
48514 /*
48515  * Based on:
48516  * Ext JS Library 1.1.1
48517  * Copyright(c) 2006-2007, Ext JS, LLC.
48518  *
48519  * Originally Released Under LGPL - original licence link has changed is not relivant.
48520  *
48521  * Fork - LGPL
48522  * <script type="text/javascript">
48523  */
48524  
48525 /**
48526  * @class Roo.form.BasicForm
48527  * @extends Roo.util.Observable
48528  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48529  * @constructor
48530  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48531  * @param {Object} config Configuration options
48532  */
48533 Roo.form.BasicForm = function(el, config){
48534     this.allItems = [];
48535     this.childForms = [];
48536     Roo.apply(this, config);
48537     /*
48538      * The Roo.form.Field items in this form.
48539      * @type MixedCollection
48540      */
48541      
48542      
48543     this.items = new Roo.util.MixedCollection(false, function(o){
48544         return o.id || (o.id = Roo.id());
48545     });
48546     this.addEvents({
48547         /**
48548          * @event beforeaction
48549          * Fires before any action is performed. Return false to cancel the action.
48550          * @param {Form} this
48551          * @param {Action} action The action to be performed
48552          */
48553         beforeaction: true,
48554         /**
48555          * @event actionfailed
48556          * Fires when an action fails.
48557          * @param {Form} this
48558          * @param {Action} action The action that failed
48559          */
48560         actionfailed : true,
48561         /**
48562          * @event actioncomplete
48563          * Fires when an action is completed.
48564          * @param {Form} this
48565          * @param {Action} action The action that completed
48566          */
48567         actioncomplete : true
48568     });
48569     if(el){
48570         this.initEl(el);
48571     }
48572     Roo.form.BasicForm.superclass.constructor.call(this);
48573     
48574     Roo.form.BasicForm.popover.apply();
48575 };
48576
48577 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48578     /**
48579      * @cfg {String} method
48580      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48581      */
48582     /**
48583      * @cfg {DataReader} reader
48584      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48585      * This is optional as there is built-in support for processing JSON.
48586      */
48587     /**
48588      * @cfg {DataReader} errorReader
48589      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48590      * This is completely optional as there is built-in support for processing JSON.
48591      */
48592     /**
48593      * @cfg {String} url
48594      * The URL to use for form actions if one isn't supplied in the action options.
48595      */
48596     /**
48597      * @cfg {Boolean} fileUpload
48598      * Set to true if this form is a file upload.
48599      */
48600      
48601     /**
48602      * @cfg {Object} baseParams
48603      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48604      */
48605      /**
48606      
48607     /**
48608      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
48609      */
48610     timeout: 30,
48611
48612     // private
48613     activeAction : null,
48614
48615     /**
48616      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
48617      * or setValues() data instead of when the form was first created.
48618      */
48619     trackResetOnLoad : false,
48620     
48621     
48622     /**
48623      * childForms - used for multi-tab forms
48624      * @type {Array}
48625      */
48626     childForms : false,
48627     
48628     /**
48629      * allItems - full list of fields.
48630      * @type {Array}
48631      */
48632     allItems : false,
48633     
48634     /**
48635      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
48636      * element by passing it or its id or mask the form itself by passing in true.
48637      * @type Mixed
48638      */
48639     waitMsgTarget : false,
48640     
48641     /**
48642      * @type Boolean
48643      */
48644     disableMask : false,
48645     
48646     /**
48647      * @cfg {Boolean} errorMask (true|false) default false
48648      */
48649     errorMask : false,
48650     
48651     /**
48652      * @cfg {Number} maskOffset Default 100
48653      */
48654     maskOffset : 100,
48655
48656     // private
48657     initEl : function(el){
48658         this.el = Roo.get(el);
48659         this.id = this.el.id || Roo.id();
48660         this.el.on('submit', this.onSubmit, this);
48661         this.el.addClass('x-form');
48662     },
48663
48664     // private
48665     onSubmit : function(e){
48666         e.stopEvent();
48667     },
48668
48669     /**
48670      * Returns true if client-side validation on the form is successful.
48671      * @return Boolean
48672      */
48673     isValid : function(){
48674         var valid = true;
48675         var target = false;
48676         this.items.each(function(f){
48677             if(f.validate()){
48678                 return;
48679             }
48680             
48681             valid = false;
48682                 
48683             if(!target && f.el.isVisible(true)){
48684                 target = f;
48685             }
48686         });
48687         
48688         if(this.errorMask && !valid){
48689             Roo.form.BasicForm.popover.mask(this, target);
48690         }
48691         
48692         return valid;
48693     },
48694     /**
48695      * Returns array of invalid form fields.
48696      * @return Array
48697      */
48698     
48699     invalidFields : function()
48700     {
48701         var ret = [];
48702         this.items.each(function(f){
48703             if(f.validate()){
48704                 return;
48705             }
48706             ret.push(f);
48707             
48708         });
48709         
48710         return ret;
48711     },
48712     
48713     
48714     /**
48715      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
48716      * @return Boolean
48717      */
48718     isDirty : function(){
48719         var dirty = false;
48720         this.items.each(function(f){
48721            if(f.isDirty()){
48722                dirty = true;
48723                return false;
48724            }
48725         });
48726         return dirty;
48727     },
48728     
48729     /**
48730      * Returns true if any fields in this form have changed since their original load. (New version)
48731      * @return Boolean
48732      */
48733     
48734     hasChanged : function()
48735     {
48736         var dirty = false;
48737         this.items.each(function(f){
48738            if(f.hasChanged()){
48739                dirty = true;
48740                return false;
48741            }
48742         });
48743         return dirty;
48744         
48745     },
48746     /**
48747      * Resets all hasChanged to 'false' -
48748      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
48749      * So hasChanged storage is only to be used for this purpose
48750      * @return Boolean
48751      */
48752     resetHasChanged : function()
48753     {
48754         this.items.each(function(f){
48755            f.resetHasChanged();
48756         });
48757         
48758     },
48759     
48760     
48761     /**
48762      * Performs a predefined action (submit or load) or custom actions you define on this form.
48763      * @param {String} actionName The name of the action type
48764      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
48765      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
48766      * accept other config options):
48767      * <pre>
48768 Property          Type             Description
48769 ----------------  ---------------  ----------------------------------------------------------------------------------
48770 url               String           The url for the action (defaults to the form's url)
48771 method            String           The form method to use (defaults to the form's method, or POST if not defined)
48772 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
48773 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
48774                                    validate the form on the client (defaults to false)
48775      * </pre>
48776      * @return {BasicForm} this
48777      */
48778     doAction : function(action, options){
48779         if(typeof action == 'string'){
48780             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
48781         }
48782         if(this.fireEvent('beforeaction', this, action) !== false){
48783             this.beforeAction(action);
48784             action.run.defer(100, action);
48785         }
48786         return this;
48787     },
48788
48789     /**
48790      * Shortcut to do a submit action.
48791      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48792      * @return {BasicForm} this
48793      */
48794     submit : function(options){
48795         this.doAction('submit', options);
48796         return this;
48797     },
48798
48799     /**
48800      * Shortcut to do a load action.
48801      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
48802      * @return {BasicForm} this
48803      */
48804     load : function(options){
48805         this.doAction('load', options);
48806         return this;
48807     },
48808
48809     /**
48810      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
48811      * @param {Record} record The record to edit
48812      * @return {BasicForm} this
48813      */
48814     updateRecord : function(record){
48815         record.beginEdit();
48816         var fs = record.fields;
48817         fs.each(function(f){
48818             var field = this.findField(f.name);
48819             if(field){
48820                 record.set(f.name, field.getValue());
48821             }
48822         }, this);
48823         record.endEdit();
48824         return this;
48825     },
48826
48827     /**
48828      * Loads an Roo.data.Record into this form.
48829      * @param {Record} record The record to load
48830      * @return {BasicForm} this
48831      */
48832     loadRecord : function(record){
48833         this.setValues(record.data);
48834         return this;
48835     },
48836
48837     // private
48838     beforeAction : function(action){
48839         var o = action.options;
48840         
48841         if(!this.disableMask) {
48842             if(this.waitMsgTarget === true){
48843                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
48844             }else if(this.waitMsgTarget){
48845                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
48846                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
48847             }else {
48848                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
48849             }
48850         }
48851         
48852          
48853     },
48854
48855     // private
48856     afterAction : function(action, success){
48857         this.activeAction = null;
48858         var o = action.options;
48859         
48860         if(!this.disableMask) {
48861             if(this.waitMsgTarget === true){
48862                 this.el.unmask();
48863             }else if(this.waitMsgTarget){
48864                 this.waitMsgTarget.unmask();
48865             }else{
48866                 Roo.MessageBox.updateProgress(1);
48867                 Roo.MessageBox.hide();
48868             }
48869         }
48870         
48871         if(success){
48872             if(o.reset){
48873                 this.reset();
48874             }
48875             Roo.callback(o.success, o.scope, [this, action]);
48876             this.fireEvent('actioncomplete', this, action);
48877             
48878         }else{
48879             
48880             // failure condition..
48881             // we have a scenario where updates need confirming.
48882             // eg. if a locking scenario exists..
48883             // we look for { errors : { needs_confirm : true }} in the response.
48884             if (
48885                 (typeof(action.result) != 'undefined')  &&
48886                 (typeof(action.result.errors) != 'undefined')  &&
48887                 (typeof(action.result.errors.needs_confirm) != 'undefined')
48888            ){
48889                 var _t = this;
48890                 Roo.MessageBox.confirm(
48891                     "Change requires confirmation",
48892                     action.result.errorMsg,
48893                     function(r) {
48894                         if (r != 'yes') {
48895                             return;
48896                         }
48897                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
48898                     }
48899                     
48900                 );
48901                 
48902                 
48903                 
48904                 return;
48905             }
48906             
48907             Roo.callback(o.failure, o.scope, [this, action]);
48908             // show an error message if no failed handler is set..
48909             if (!this.hasListener('actionfailed')) {
48910                 Roo.MessageBox.alert("Error",
48911                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
48912                         action.result.errorMsg :
48913                         "Saving Failed, please check your entries or try again"
48914                 );
48915             }
48916             
48917             this.fireEvent('actionfailed', this, action);
48918         }
48919         
48920     },
48921
48922     /**
48923      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
48924      * @param {String} id The value to search for
48925      * @return Field
48926      */
48927     findField : function(id){
48928         var field = this.items.get(id);
48929         if(!field){
48930             this.items.each(function(f){
48931                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48932                     field = f;
48933                     return false;
48934                 }
48935             });
48936         }
48937         return field || null;
48938     },
48939
48940     /**
48941      * Add a secondary form to this one, 
48942      * Used to provide tabbed forms. One form is primary, with hidden values 
48943      * which mirror the elements from the other forms.
48944      * 
48945      * @param {Roo.form.Form} form to add.
48946      * 
48947      */
48948     addForm : function(form)
48949     {
48950        
48951         if (this.childForms.indexOf(form) > -1) {
48952             // already added..
48953             return;
48954         }
48955         this.childForms.push(form);
48956         var n = '';
48957         Roo.each(form.allItems, function (fe) {
48958             
48959             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48960             if (this.findField(n)) { // already added..
48961                 return;
48962             }
48963             var add = new Roo.form.Hidden({
48964                 name : n
48965             });
48966             add.render(this.el);
48967             
48968             this.add( add );
48969         }, this);
48970         
48971     },
48972     /**
48973      * Mark fields in this form invalid in bulk.
48974      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48975      * @return {BasicForm} this
48976      */
48977     markInvalid : function(errors){
48978         if(errors instanceof Array){
48979             for(var i = 0, len = errors.length; i < len; i++){
48980                 var fieldError = errors[i];
48981                 var f = this.findField(fieldError.id);
48982                 if(f){
48983                     f.markInvalid(fieldError.msg);
48984                 }
48985             }
48986         }else{
48987             var field, id;
48988             for(id in errors){
48989                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48990                     field.markInvalid(errors[id]);
48991                 }
48992             }
48993         }
48994         Roo.each(this.childForms || [], function (f) {
48995             f.markInvalid(errors);
48996         });
48997         
48998         return this;
48999     },
49000
49001     /**
49002      * Set values for fields in this form in bulk.
49003      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49004      * @return {BasicForm} this
49005      */
49006     setValues : function(values){
49007         if(values instanceof Array){ // array of objects
49008             for(var i = 0, len = values.length; i < len; i++){
49009                 var v = values[i];
49010                 var f = this.findField(v.id);
49011                 if(f){
49012                     f.setValue(v.value);
49013                     if(this.trackResetOnLoad){
49014                         f.originalValue = f.getValue();
49015                     }
49016                 }
49017             }
49018         }else{ // object hash
49019             var field, id;
49020             for(id in values){
49021                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49022                     
49023                     if (field.setFromData && 
49024                         field.valueField && 
49025                         field.displayField &&
49026                         // combos' with local stores can 
49027                         // be queried via setValue()
49028                         // to set their value..
49029                         (field.store && !field.store.isLocal)
49030                         ) {
49031                         // it's a combo
49032                         var sd = { };
49033                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49034                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49035                         field.setFromData(sd);
49036                         
49037                     } else {
49038                         field.setValue(values[id]);
49039                     }
49040                     
49041                     
49042                     if(this.trackResetOnLoad){
49043                         field.originalValue = field.getValue();
49044                     }
49045                 }
49046             }
49047         }
49048         this.resetHasChanged();
49049         
49050         
49051         Roo.each(this.childForms || [], function (f) {
49052             f.setValues(values);
49053             f.resetHasChanged();
49054         });
49055                 
49056         return this;
49057     },
49058  
49059     /**
49060      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49061      * they are returned as an array.
49062      * @param {Boolean} asString
49063      * @return {Object}
49064      */
49065     getValues : function(asString)
49066     {
49067         if (this.childForms) {
49068             // copy values from the child forms
49069             Roo.each(this.childForms, function (f) {
49070                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
49071             }, this);
49072         }
49073         
49074         // use formdata
49075         if (typeof(FormData) != 'undefined' && asString !== true) {
49076             // this relies on a 'recent' version of chrome apparently...
49077             try {
49078                 var fd = (new FormData(this.el.dom)).entries();
49079                 var ret = {};
49080                 var ent = fd.next();
49081                 while (!ent.done) {
49082                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49083                     ent = fd.next();
49084                 };
49085                 return ret;
49086             } catch(e) {
49087                 
49088             }
49089             
49090         }
49091         
49092         
49093         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49094         if(asString === true){
49095             return fs;
49096         }
49097         return Roo.urlDecode(fs);
49098     },
49099     
49100     /**
49101      * Returns the fields in this form as an object with key/value pairs. 
49102      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49103      * Normally this will not return readOnly data 
49104      * @param {Boolean} with_readonly return readonly field data.
49105      * @return {Object}
49106      */
49107     getFieldValues : function(with_readonly)
49108     {
49109         if (this.childForms) {
49110             // copy values from the child forms
49111             // should this call getFieldValues - probably not as we do not currently copy
49112             // hidden fields when we generate..
49113             Roo.each(this.childForms, function (f) {
49114                 this.setValues(f.getFieldValues());
49115             }, this);
49116         }
49117         
49118         var ret = {};
49119         this.items.each(function(f){
49120             
49121             if (f.readOnly && with_readonly !== true) {
49122                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
49123                         // if a subform contains a copy of them.
49124                         // if you have subforms with the same editable data, you will need to copy the data back
49125                         // and forth.
49126             }
49127             
49128             if (!f.getName()) {
49129                 return;
49130             }
49131             var v = f.getValue();
49132             if (f.inputType =='radio') {
49133                 if (typeof(ret[f.getName()]) == 'undefined') {
49134                     ret[f.getName()] = ''; // empty..
49135                 }
49136                 
49137                 if (!f.el.dom.checked) {
49138                     return;
49139                     
49140                 }
49141                 v = f.el.dom.value;
49142                 
49143             }
49144             
49145             // not sure if this supported any more..
49146             if ((typeof(v) == 'object') && f.getRawValue) {
49147                 v = f.getRawValue() ; // dates..
49148             }
49149             // combo boxes where name != hiddenName...
49150             if (f.name != f.getName()) {
49151                 ret[f.name] = f.getRawValue();
49152             }
49153             ret[f.getName()] = v;
49154         });
49155         
49156         return ret;
49157     },
49158
49159     /**
49160      * Clears all invalid messages in this form.
49161      * @return {BasicForm} this
49162      */
49163     clearInvalid : function(){
49164         this.items.each(function(f){
49165            f.clearInvalid();
49166         });
49167         
49168         Roo.each(this.childForms || [], function (f) {
49169             f.clearInvalid();
49170         });
49171         
49172         
49173         return this;
49174     },
49175
49176     /**
49177      * Resets this form.
49178      * @return {BasicForm} this
49179      */
49180     reset : function(){
49181         this.items.each(function(f){
49182             f.reset();
49183         });
49184         
49185         Roo.each(this.childForms || [], function (f) {
49186             f.reset();
49187         });
49188         this.resetHasChanged();
49189         
49190         return this;
49191     },
49192
49193     /**
49194      * Add Roo.form components to this form.
49195      * @param {Field} field1
49196      * @param {Field} field2 (optional)
49197      * @param {Field} etc (optional)
49198      * @return {BasicForm} this
49199      */
49200     add : function(){
49201         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49202         return this;
49203     },
49204
49205
49206     /**
49207      * Removes a field from the items collection (does NOT remove its markup).
49208      * @param {Field} field
49209      * @return {BasicForm} this
49210      */
49211     remove : function(field){
49212         this.items.remove(field);
49213         return this;
49214     },
49215
49216     /**
49217      * Looks at the fields in this form, checks them for an id attribute,
49218      * and calls applyTo on the existing dom element with that id.
49219      * @return {BasicForm} this
49220      */
49221     render : function(){
49222         this.items.each(function(f){
49223             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49224                 f.applyTo(f.id);
49225             }
49226         });
49227         return this;
49228     },
49229
49230     /**
49231      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49232      * @param {Object} values
49233      * @return {BasicForm} this
49234      */
49235     applyToFields : function(o){
49236         this.items.each(function(f){
49237            Roo.apply(f, o);
49238         });
49239         return this;
49240     },
49241
49242     /**
49243      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49244      * @param {Object} values
49245      * @return {BasicForm} this
49246      */
49247     applyIfToFields : function(o){
49248         this.items.each(function(f){
49249            Roo.applyIf(f, o);
49250         });
49251         return this;
49252     }
49253 });
49254
49255 // back compat
49256 Roo.BasicForm = Roo.form.BasicForm;
49257
49258 Roo.apply(Roo.form.BasicForm, {
49259     
49260     popover : {
49261         
49262         padding : 5,
49263         
49264         isApplied : false,
49265         
49266         isMasked : false,
49267         
49268         form : false,
49269         
49270         target : false,
49271         
49272         intervalID : false,
49273         
49274         maskEl : false,
49275         
49276         apply : function()
49277         {
49278             if(this.isApplied){
49279                 return;
49280             }
49281             
49282             this.maskEl = {
49283                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49284                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49285                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49286                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49287             };
49288             
49289             this.maskEl.top.enableDisplayMode("block");
49290             this.maskEl.left.enableDisplayMode("block");
49291             this.maskEl.bottom.enableDisplayMode("block");
49292             this.maskEl.right.enableDisplayMode("block");
49293             
49294             Roo.get(document.body).on('click', function(){
49295                 this.unmask();
49296             }, this);
49297             
49298             Roo.get(document.body).on('touchstart', function(){
49299                 this.unmask();
49300             }, this);
49301             
49302             this.isApplied = true
49303         },
49304         
49305         mask : function(form, target)
49306         {
49307             this.form = form;
49308             
49309             this.target = target;
49310             
49311             if(!this.form.errorMask || !target.el){
49312                 return;
49313             }
49314             
49315             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49316             
49317             var ot = this.target.el.calcOffsetsTo(scrollable);
49318             
49319             var scrollTo = ot[1] - this.form.maskOffset;
49320             
49321             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49322             
49323             scrollable.scrollTo('top', scrollTo);
49324             
49325             var el = this.target.wrap || this.target.el;
49326             
49327             var box = el.getBox();
49328             
49329             this.maskEl.top.setStyle('position', 'absolute');
49330             this.maskEl.top.setStyle('z-index', 10000);
49331             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49332             this.maskEl.top.setLeft(0);
49333             this.maskEl.top.setTop(0);
49334             this.maskEl.top.show();
49335             
49336             this.maskEl.left.setStyle('position', 'absolute');
49337             this.maskEl.left.setStyle('z-index', 10000);
49338             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49339             this.maskEl.left.setLeft(0);
49340             this.maskEl.left.setTop(box.y - this.padding);
49341             this.maskEl.left.show();
49342
49343             this.maskEl.bottom.setStyle('position', 'absolute');
49344             this.maskEl.bottom.setStyle('z-index', 10000);
49345             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49346             this.maskEl.bottom.setLeft(0);
49347             this.maskEl.bottom.setTop(box.bottom + this.padding);
49348             this.maskEl.bottom.show();
49349
49350             this.maskEl.right.setStyle('position', 'absolute');
49351             this.maskEl.right.setStyle('z-index', 10000);
49352             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49353             this.maskEl.right.setLeft(box.right + this.padding);
49354             this.maskEl.right.setTop(box.y - this.padding);
49355             this.maskEl.right.show();
49356
49357             this.intervalID = window.setInterval(function() {
49358                 Roo.form.BasicForm.popover.unmask();
49359             }, 10000);
49360
49361             window.onwheel = function(){ return false;};
49362             
49363             (function(){ this.isMasked = true; }).defer(500, this);
49364             
49365         },
49366         
49367         unmask : function()
49368         {
49369             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49370                 return;
49371             }
49372             
49373             this.maskEl.top.setStyle('position', 'absolute');
49374             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49375             this.maskEl.top.hide();
49376
49377             this.maskEl.left.setStyle('position', 'absolute');
49378             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49379             this.maskEl.left.hide();
49380
49381             this.maskEl.bottom.setStyle('position', 'absolute');
49382             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49383             this.maskEl.bottom.hide();
49384
49385             this.maskEl.right.setStyle('position', 'absolute');
49386             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49387             this.maskEl.right.hide();
49388             
49389             window.onwheel = function(){ return true;};
49390             
49391             if(this.intervalID){
49392                 window.clearInterval(this.intervalID);
49393                 this.intervalID = false;
49394             }
49395             
49396             this.isMasked = false;
49397             
49398         }
49399         
49400     }
49401     
49402 });/*
49403  * Based on:
49404  * Ext JS Library 1.1.1
49405  * Copyright(c) 2006-2007, Ext JS, LLC.
49406  *
49407  * Originally Released Under LGPL - original licence link has changed is not relivant.
49408  *
49409  * Fork - LGPL
49410  * <script type="text/javascript">
49411  */
49412
49413 /**
49414  * @class Roo.form.Form
49415  * @extends Roo.form.BasicForm
49416  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49417  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49418  * @constructor
49419  * @param {Object} config Configuration options
49420  */
49421 Roo.form.Form = function(config){
49422     var xitems =  [];
49423     if (config.items) {
49424         xitems = config.items;
49425         delete config.items;
49426     }
49427    
49428     
49429     Roo.form.Form.superclass.constructor.call(this, null, config);
49430     this.url = this.url || this.action;
49431     if(!this.root){
49432         this.root = new Roo.form.Layout(Roo.applyIf({
49433             id: Roo.id()
49434         }, config));
49435     }
49436     this.active = this.root;
49437     /**
49438      * Array of all the buttons that have been added to this form via {@link addButton}
49439      * @type Array
49440      */
49441     this.buttons = [];
49442     this.allItems = [];
49443     this.addEvents({
49444         /**
49445          * @event clientvalidation
49446          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49447          * @param {Form} this
49448          * @param {Boolean} valid true if the form has passed client-side validation
49449          */
49450         clientvalidation: true,
49451         /**
49452          * @event rendered
49453          * Fires when the form is rendered
49454          * @param {Roo.form.Form} form
49455          */
49456         rendered : true
49457     });
49458     
49459     if (this.progressUrl) {
49460             // push a hidden field onto the list of fields..
49461             this.addxtype( {
49462                     xns: Roo.form, 
49463                     xtype : 'Hidden', 
49464                     name : 'UPLOAD_IDENTIFIER' 
49465             });
49466         }
49467         
49468     
49469     Roo.each(xitems, this.addxtype, this);
49470     
49471 };
49472
49473 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49474      /**
49475      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49476      */
49477     
49478     /**
49479      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49480      */
49481     /**
49482      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49483      */
49484     /**
49485      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49486      */
49487     buttonAlign:'center',
49488
49489     /**
49490      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49491      */
49492     minButtonWidth:75,
49493
49494     /**
49495      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49496      * This property cascades to child containers if not set.
49497      */
49498     labelAlign:'left',
49499
49500     /**
49501      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49502      * fires a looping event with that state. This is required to bind buttons to the valid
49503      * state using the config value formBind:true on the button.
49504      */
49505     monitorValid : false,
49506
49507     /**
49508      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49509      */
49510     monitorPoll : 200,
49511     
49512     /**
49513      * @cfg {String} progressUrl - Url to return progress data 
49514      */
49515     
49516     progressUrl : false,
49517     /**
49518      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49519      * sending a formdata with extra parameters - eg uploaded elements.
49520      */
49521     
49522     formData : false,
49523     
49524     /**
49525      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49526      * fields are added and the column is closed. If no fields are passed the column remains open
49527      * until end() is called.
49528      * @param {Object} config The config to pass to the column
49529      * @param {Field} field1 (optional)
49530      * @param {Field} field2 (optional)
49531      * @param {Field} etc (optional)
49532      * @return Column The column container object
49533      */
49534     column : function(c){
49535         var col = new Roo.form.Column(c);
49536         this.start(col);
49537         if(arguments.length > 1){ // duplicate code required because of Opera
49538             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49539             this.end();
49540         }
49541         return col;
49542     },
49543
49544     /**
49545      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49546      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49547      * until end() is called.
49548      * @param {Object} config The config to pass to the fieldset
49549      * @param {Field} field1 (optional)
49550      * @param {Field} field2 (optional)
49551      * @param {Field} etc (optional)
49552      * @return FieldSet The fieldset container object
49553      */
49554     fieldset : function(c){
49555         var fs = new Roo.form.FieldSet(c);
49556         this.start(fs);
49557         if(arguments.length > 1){ // duplicate code required because of Opera
49558             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49559             this.end();
49560         }
49561         return fs;
49562     },
49563
49564     /**
49565      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49566      * fields are added and the container is closed. If no fields are passed the container remains open
49567      * until end() is called.
49568      * @param {Object} config The config to pass to the Layout
49569      * @param {Field} field1 (optional)
49570      * @param {Field} field2 (optional)
49571      * @param {Field} etc (optional)
49572      * @return Layout The container object
49573      */
49574     container : function(c){
49575         var l = new Roo.form.Layout(c);
49576         this.start(l);
49577         if(arguments.length > 1){ // duplicate code required because of Opera
49578             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49579             this.end();
49580         }
49581         return l;
49582     },
49583
49584     /**
49585      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49586      * @param {Object} container A Roo.form.Layout or subclass of Layout
49587      * @return {Form} this
49588      */
49589     start : function(c){
49590         // cascade label info
49591         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49592         this.active.stack.push(c);
49593         c.ownerCt = this.active;
49594         this.active = c;
49595         return this;
49596     },
49597
49598     /**
49599      * Closes the current open container
49600      * @return {Form} this
49601      */
49602     end : function(){
49603         if(this.active == this.root){
49604             return this;
49605         }
49606         this.active = this.active.ownerCt;
49607         return this;
49608     },
49609
49610     /**
49611      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49612      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49613      * as the label of the field.
49614      * @param {Field} field1
49615      * @param {Field} field2 (optional)
49616      * @param {Field} etc. (optional)
49617      * @return {Form} this
49618      */
49619     add : function(){
49620         this.active.stack.push.apply(this.active.stack, arguments);
49621         this.allItems.push.apply(this.allItems,arguments);
49622         var r = [];
49623         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
49624             if(a[i].isFormField){
49625                 r.push(a[i]);
49626             }
49627         }
49628         if(r.length > 0){
49629             Roo.form.Form.superclass.add.apply(this, r);
49630         }
49631         return this;
49632     },
49633     
49634
49635     
49636     
49637     
49638      /**
49639      * Find any element that has been added to a form, using it's ID or name
49640      * This can include framesets, columns etc. along with regular fields..
49641      * @param {String} id - id or name to find.
49642      
49643      * @return {Element} e - or false if nothing found.
49644      */
49645     findbyId : function(id)
49646     {
49647         var ret = false;
49648         if (!id) {
49649             return ret;
49650         }
49651         Roo.each(this.allItems, function(f){
49652             if (f.id == id || f.name == id ){
49653                 ret = f;
49654                 return false;
49655             }
49656         });
49657         return ret;
49658     },
49659
49660     
49661     
49662     /**
49663      * Render this form into the passed container. This should only be called once!
49664      * @param {String/HTMLElement/Element} container The element this component should be rendered into
49665      * @return {Form} this
49666      */
49667     render : function(ct)
49668     {
49669         
49670         
49671         
49672         ct = Roo.get(ct);
49673         var o = this.autoCreate || {
49674             tag: 'form',
49675             method : this.method || 'POST',
49676             id : this.id || Roo.id()
49677         };
49678         this.initEl(ct.createChild(o));
49679
49680         this.root.render(this.el);
49681         
49682        
49683              
49684         this.items.each(function(f){
49685             f.render('x-form-el-'+f.id);
49686         });
49687
49688         if(this.buttons.length > 0){
49689             // tables are required to maintain order and for correct IE layout
49690             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
49691                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
49692                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
49693             }}, null, true);
49694             var tr = tb.getElementsByTagName('tr')[0];
49695             for(var i = 0, len = this.buttons.length; i < len; i++) {
49696                 var b = this.buttons[i];
49697                 var td = document.createElement('td');
49698                 td.className = 'x-form-btn-td';
49699                 b.render(tr.appendChild(td));
49700             }
49701         }
49702         if(this.monitorValid){ // initialize after render
49703             this.startMonitoring();
49704         }
49705         this.fireEvent('rendered', this);
49706         return this;
49707     },
49708
49709     /**
49710      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
49711      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
49712      * object or a valid Roo.DomHelper element config
49713      * @param {Function} handler The function called when the button is clicked
49714      * @param {Object} scope (optional) The scope of the handler function
49715      * @return {Roo.Button}
49716      */
49717     addButton : function(config, handler, scope){
49718         var bc = {
49719             handler: handler,
49720             scope: scope,
49721             minWidth: this.minButtonWidth,
49722             hideParent:true
49723         };
49724         if(typeof config == "string"){
49725             bc.text = config;
49726         }else{
49727             Roo.apply(bc, config);
49728         }
49729         var btn = new Roo.Button(null, bc);
49730         this.buttons.push(btn);
49731         return btn;
49732     },
49733
49734      /**
49735      * Adds a series of form elements (using the xtype property as the factory method.
49736      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
49737      * @param {Object} config 
49738      */
49739     
49740     addxtype : function()
49741     {
49742         var ar = Array.prototype.slice.call(arguments, 0);
49743         var ret = false;
49744         for(var i = 0; i < ar.length; i++) {
49745             if (!ar[i]) {
49746                 continue; // skip -- if this happends something invalid got sent, we 
49747                 // should ignore it, as basically that interface element will not show up
49748                 // and that should be pretty obvious!!
49749             }
49750             
49751             if (Roo.form[ar[i].xtype]) {
49752                 ar[i].form = this;
49753                 var fe = Roo.factory(ar[i], Roo.form);
49754                 if (!ret) {
49755                     ret = fe;
49756                 }
49757                 fe.form = this;
49758                 if (fe.store) {
49759                     fe.store.form = this;
49760                 }
49761                 if (fe.isLayout) {  
49762                          
49763                     this.start(fe);
49764                     this.allItems.push(fe);
49765                     if (fe.items && fe.addxtype) {
49766                         fe.addxtype.apply(fe, fe.items);
49767                         delete fe.items;
49768                     }
49769                      this.end();
49770                     continue;
49771                 }
49772                 
49773                 
49774                  
49775                 this.add(fe);
49776               //  console.log('adding ' + ar[i].xtype);
49777             }
49778             if (ar[i].xtype == 'Button') {  
49779                 //console.log('adding button');
49780                 //console.log(ar[i]);
49781                 this.addButton(ar[i]);
49782                 this.allItems.push(fe);
49783                 continue;
49784             }
49785             
49786             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
49787                 alert('end is not supported on xtype any more, use items');
49788             //    this.end();
49789             //    //console.log('adding end');
49790             }
49791             
49792         }
49793         return ret;
49794     },
49795     
49796     /**
49797      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
49798      * option "monitorValid"
49799      */
49800     startMonitoring : function(){
49801         if(!this.bound){
49802             this.bound = true;
49803             Roo.TaskMgr.start({
49804                 run : this.bindHandler,
49805                 interval : this.monitorPoll || 200,
49806                 scope: this
49807             });
49808         }
49809     },
49810
49811     /**
49812      * Stops monitoring of the valid state of this form
49813      */
49814     stopMonitoring : function(){
49815         this.bound = false;
49816     },
49817
49818     // private
49819     bindHandler : function(){
49820         if(!this.bound){
49821             return false; // stops binding
49822         }
49823         var valid = true;
49824         this.items.each(function(f){
49825             if(!f.isValid(true)){
49826                 valid = false;
49827                 return false;
49828             }
49829         });
49830         for(var i = 0, len = this.buttons.length; i < len; i++){
49831             var btn = this.buttons[i];
49832             if(btn.formBind === true && btn.disabled === valid){
49833                 btn.setDisabled(!valid);
49834             }
49835         }
49836         this.fireEvent('clientvalidation', this, valid);
49837     }
49838     
49839     
49840     
49841     
49842     
49843     
49844     
49845     
49846 });
49847
49848
49849 // back compat
49850 Roo.Form = Roo.form.Form;
49851 /*
49852  * Based on:
49853  * Ext JS Library 1.1.1
49854  * Copyright(c) 2006-2007, Ext JS, LLC.
49855  *
49856  * Originally Released Under LGPL - original licence link has changed is not relivant.
49857  *
49858  * Fork - LGPL
49859  * <script type="text/javascript">
49860  */
49861
49862 // as we use this in bootstrap.
49863 Roo.namespace('Roo.form');
49864  /**
49865  * @class Roo.form.Action
49866  * Internal Class used to handle form actions
49867  * @constructor
49868  * @param {Roo.form.BasicForm} el The form element or its id
49869  * @param {Object} config Configuration options
49870  */
49871
49872  
49873  
49874 // define the action interface
49875 Roo.form.Action = function(form, options){
49876     this.form = form;
49877     this.options = options || {};
49878 };
49879 /**
49880  * Client Validation Failed
49881  * @const 
49882  */
49883 Roo.form.Action.CLIENT_INVALID = 'client';
49884 /**
49885  * Server Validation Failed
49886  * @const 
49887  */
49888 Roo.form.Action.SERVER_INVALID = 'server';
49889  /**
49890  * Connect to Server Failed
49891  * @const 
49892  */
49893 Roo.form.Action.CONNECT_FAILURE = 'connect';
49894 /**
49895  * Reading Data from Server Failed
49896  * @const 
49897  */
49898 Roo.form.Action.LOAD_FAILURE = 'load';
49899
49900 Roo.form.Action.prototype = {
49901     type : 'default',
49902     failureType : undefined,
49903     response : undefined,
49904     result : undefined,
49905
49906     // interface method
49907     run : function(options){
49908
49909     },
49910
49911     // interface method
49912     success : function(response){
49913
49914     },
49915
49916     // interface method
49917     handleResponse : function(response){
49918
49919     },
49920
49921     // default connection failure
49922     failure : function(response){
49923         
49924         this.response = response;
49925         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49926         this.form.afterAction(this, false);
49927     },
49928
49929     processResponse : function(response){
49930         this.response = response;
49931         if(!response.responseText){
49932             return true;
49933         }
49934         this.result = this.handleResponse(response);
49935         return this.result;
49936     },
49937
49938     // utility functions used internally
49939     getUrl : function(appendParams){
49940         var url = this.options.url || this.form.url || this.form.el.dom.action;
49941         if(appendParams){
49942             var p = this.getParams();
49943             if(p){
49944                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
49945             }
49946         }
49947         return url;
49948     },
49949
49950     getMethod : function(){
49951         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49952     },
49953
49954     getParams : function(){
49955         var bp = this.form.baseParams;
49956         var p = this.options.params;
49957         if(p){
49958             if(typeof p == "object"){
49959                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49960             }else if(typeof p == 'string' && bp){
49961                 p += '&' + Roo.urlEncode(bp);
49962             }
49963         }else if(bp){
49964             p = Roo.urlEncode(bp);
49965         }
49966         return p;
49967     },
49968
49969     createCallback : function(){
49970         return {
49971             success: this.success,
49972             failure: this.failure,
49973             scope: this,
49974             timeout: (this.form.timeout*1000),
49975             upload: this.form.fileUpload ? this.success : undefined
49976         };
49977     }
49978 };
49979
49980 Roo.form.Action.Submit = function(form, options){
49981     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49982 };
49983
49984 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49985     type : 'submit',
49986
49987     haveProgress : false,
49988     uploadComplete : false,
49989     
49990     // uploadProgress indicator.
49991     uploadProgress : function()
49992     {
49993         if (!this.form.progressUrl) {
49994             return;
49995         }
49996         
49997         if (!this.haveProgress) {
49998             Roo.MessageBox.progress("Uploading", "Uploading");
49999         }
50000         if (this.uploadComplete) {
50001            Roo.MessageBox.hide();
50002            return;
50003         }
50004         
50005         this.haveProgress = true;
50006    
50007         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50008         
50009         var c = new Roo.data.Connection();
50010         c.request({
50011             url : this.form.progressUrl,
50012             params: {
50013                 id : uid
50014             },
50015             method: 'GET',
50016             success : function(req){
50017                //console.log(data);
50018                 var rdata = false;
50019                 var edata;
50020                 try  {
50021                    rdata = Roo.decode(req.responseText)
50022                 } catch (e) {
50023                     Roo.log("Invalid data from server..");
50024                     Roo.log(edata);
50025                     return;
50026                 }
50027                 if (!rdata || !rdata.success) {
50028                     Roo.log(rdata);
50029                     Roo.MessageBox.alert(Roo.encode(rdata));
50030                     return;
50031                 }
50032                 var data = rdata.data;
50033                 
50034                 if (this.uploadComplete) {
50035                    Roo.MessageBox.hide();
50036                    return;
50037                 }
50038                    
50039                 if (data){
50040                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50041                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50042                     );
50043                 }
50044                 this.uploadProgress.defer(2000,this);
50045             },
50046        
50047             failure: function(data) {
50048                 Roo.log('progress url failed ');
50049                 Roo.log(data);
50050             },
50051             scope : this
50052         });
50053            
50054     },
50055     
50056     
50057     run : function()
50058     {
50059         // run get Values on the form, so it syncs any secondary forms.
50060         this.form.getValues();
50061         
50062         var o = this.options;
50063         var method = this.getMethod();
50064         var isPost = method == 'POST';
50065         if(o.clientValidation === false || this.form.isValid()){
50066             
50067             if (this.form.progressUrl) {
50068                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50069                     (new Date() * 1) + '' + Math.random());
50070                     
50071             } 
50072             
50073             
50074             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50075                 form:this.form.el.dom,
50076                 url:this.getUrl(!isPost),
50077                 method: method,
50078                 params:isPost ? this.getParams() : null,
50079                 isUpload: this.form.fileUpload,
50080                 formData : this.form.formData
50081             }));
50082             
50083             this.uploadProgress();
50084
50085         }else if (o.clientValidation !== false){ // client validation failed
50086             this.failureType = Roo.form.Action.CLIENT_INVALID;
50087             this.form.afterAction(this, false);
50088         }
50089     },
50090
50091     success : function(response)
50092     {
50093         this.uploadComplete= true;
50094         if (this.haveProgress) {
50095             Roo.MessageBox.hide();
50096         }
50097         
50098         
50099         var result = this.processResponse(response);
50100         if(result === true || result.success){
50101             this.form.afterAction(this, true);
50102             return;
50103         }
50104         if(result.errors){
50105             this.form.markInvalid(result.errors);
50106             this.failureType = Roo.form.Action.SERVER_INVALID;
50107         }
50108         this.form.afterAction(this, false);
50109     },
50110     failure : function(response)
50111     {
50112         this.uploadComplete= true;
50113         if (this.haveProgress) {
50114             Roo.MessageBox.hide();
50115         }
50116         
50117         this.response = response;
50118         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50119         this.form.afterAction(this, false);
50120     },
50121     
50122     handleResponse : function(response){
50123         if(this.form.errorReader){
50124             var rs = this.form.errorReader.read(response);
50125             var errors = [];
50126             if(rs.records){
50127                 for(var i = 0, len = rs.records.length; i < len; i++) {
50128                     var r = rs.records[i];
50129                     errors[i] = r.data;
50130                 }
50131             }
50132             if(errors.length < 1){
50133                 errors = null;
50134             }
50135             return {
50136                 success : rs.success,
50137                 errors : errors
50138             };
50139         }
50140         var ret = false;
50141         try {
50142             ret = Roo.decode(response.responseText);
50143         } catch (e) {
50144             ret = {
50145                 success: false,
50146                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50147                 errors : []
50148             };
50149         }
50150         return ret;
50151         
50152     }
50153 });
50154
50155
50156 Roo.form.Action.Load = function(form, options){
50157     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50158     this.reader = this.form.reader;
50159 };
50160
50161 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
50162     type : 'load',
50163
50164     run : function(){
50165         
50166         Roo.Ajax.request(Roo.apply(
50167                 this.createCallback(), {
50168                     method:this.getMethod(),
50169                     url:this.getUrl(false),
50170                     params:this.getParams()
50171         }));
50172     },
50173
50174     success : function(response){
50175         
50176         var result = this.processResponse(response);
50177         if(result === true || !result.success || !result.data){
50178             this.failureType = Roo.form.Action.LOAD_FAILURE;
50179             this.form.afterAction(this, false);
50180             return;
50181         }
50182         this.form.clearInvalid();
50183         this.form.setValues(result.data);
50184         this.form.afterAction(this, true);
50185     },
50186
50187     handleResponse : function(response){
50188         if(this.form.reader){
50189             var rs = this.form.reader.read(response);
50190             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50191             return {
50192                 success : rs.success,
50193                 data : data
50194             };
50195         }
50196         return Roo.decode(response.responseText);
50197     }
50198 });
50199
50200 Roo.form.Action.ACTION_TYPES = {
50201     'load' : Roo.form.Action.Load,
50202     'submit' : Roo.form.Action.Submit
50203 };/*
50204  * Based on:
50205  * Ext JS Library 1.1.1
50206  * Copyright(c) 2006-2007, Ext JS, LLC.
50207  *
50208  * Originally Released Under LGPL - original licence link has changed is not relivant.
50209  *
50210  * Fork - LGPL
50211  * <script type="text/javascript">
50212  */
50213  
50214 /**
50215  * @class Roo.form.Layout
50216  * @extends Roo.Component
50217  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
50218  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50219  * @constructor
50220  * @param {Object} config Configuration options
50221  */
50222 Roo.form.Layout = function(config){
50223     var xitems = [];
50224     if (config.items) {
50225         xitems = config.items;
50226         delete config.items;
50227     }
50228     Roo.form.Layout.superclass.constructor.call(this, config);
50229     this.stack = [];
50230     Roo.each(xitems, this.addxtype, this);
50231      
50232 };
50233
50234 Roo.extend(Roo.form.Layout, Roo.Component, {
50235     /**
50236      * @cfg {String/Object} autoCreate
50237      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50238      */
50239     /**
50240      * @cfg {String/Object/Function} style
50241      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50242      * a function which returns such a specification.
50243      */
50244     /**
50245      * @cfg {String} labelAlign
50246      * Valid values are "left," "top" and "right" (defaults to "left")
50247      */
50248     /**
50249      * @cfg {Number} labelWidth
50250      * Fixed width in pixels of all field labels (defaults to undefined)
50251      */
50252     /**
50253      * @cfg {Boolean} clear
50254      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50255      */
50256     clear : true,
50257     /**
50258      * @cfg {String} labelSeparator
50259      * The separator to use after field labels (defaults to ':')
50260      */
50261     labelSeparator : ':',
50262     /**
50263      * @cfg {Boolean} hideLabels
50264      * True to suppress the display of field labels in this layout (defaults to false)
50265      */
50266     hideLabels : false,
50267
50268     // private
50269     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50270     
50271     isLayout : true,
50272     
50273     // private
50274     onRender : function(ct, position){
50275         if(this.el){ // from markup
50276             this.el = Roo.get(this.el);
50277         }else {  // generate
50278             var cfg = this.getAutoCreate();
50279             this.el = ct.createChild(cfg, position);
50280         }
50281         if(this.style){
50282             this.el.applyStyles(this.style);
50283         }
50284         if(this.labelAlign){
50285             this.el.addClass('x-form-label-'+this.labelAlign);
50286         }
50287         if(this.hideLabels){
50288             this.labelStyle = "display:none";
50289             this.elementStyle = "padding-left:0;";
50290         }else{
50291             if(typeof this.labelWidth == 'number'){
50292                 this.labelStyle = "width:"+this.labelWidth+"px;";
50293                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50294             }
50295             if(this.labelAlign == 'top'){
50296                 this.labelStyle = "width:auto;";
50297                 this.elementStyle = "padding-left:0;";
50298             }
50299         }
50300         var stack = this.stack;
50301         var slen = stack.length;
50302         if(slen > 0){
50303             if(!this.fieldTpl){
50304                 var t = new Roo.Template(
50305                     '<div class="x-form-item {5}">',
50306                         '<label for="{0}" style="{2}">{1}{4}</label>',
50307                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50308                         '</div>',
50309                     '</div><div class="x-form-clear-left"></div>'
50310                 );
50311                 t.disableFormats = true;
50312                 t.compile();
50313                 Roo.form.Layout.prototype.fieldTpl = t;
50314             }
50315             for(var i = 0; i < slen; i++) {
50316                 if(stack[i].isFormField){
50317                     this.renderField(stack[i]);
50318                 }else{
50319                     this.renderComponent(stack[i]);
50320                 }
50321             }
50322         }
50323         if(this.clear){
50324             this.el.createChild({cls:'x-form-clear'});
50325         }
50326     },
50327
50328     // private
50329     renderField : function(f){
50330         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50331                f.id, //0
50332                f.fieldLabel, //1
50333                f.labelStyle||this.labelStyle||'', //2
50334                this.elementStyle||'', //3
50335                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50336                f.itemCls||this.itemCls||''  //5
50337        ], true).getPrevSibling());
50338     },
50339
50340     // private
50341     renderComponent : function(c){
50342         c.render(c.isLayout ? this.el : this.el.createChild());    
50343     },
50344     /**
50345      * Adds a object form elements (using the xtype property as the factory method.)
50346      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50347      * @param {Object} config 
50348      */
50349     addxtype : function(o)
50350     {
50351         // create the lement.
50352         o.form = this.form;
50353         var fe = Roo.factory(o, Roo.form);
50354         this.form.allItems.push(fe);
50355         this.stack.push(fe);
50356         
50357         if (fe.isFormField) {
50358             this.form.items.add(fe);
50359         }
50360          
50361         return fe;
50362     }
50363 });
50364
50365 /**
50366  * @class Roo.form.Column
50367  * @extends Roo.form.Layout
50368  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
50369  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50370  * @constructor
50371  * @param {Object} config Configuration options
50372  */
50373 Roo.form.Column = function(config){
50374     Roo.form.Column.superclass.constructor.call(this, config);
50375 };
50376
50377 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50378     /**
50379      * @cfg {Number/String} width
50380      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50381      */
50382     /**
50383      * @cfg {String/Object} autoCreate
50384      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50385      */
50386
50387     // private
50388     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50389
50390     // private
50391     onRender : function(ct, position){
50392         Roo.form.Column.superclass.onRender.call(this, ct, position);
50393         if(this.width){
50394             this.el.setWidth(this.width);
50395         }
50396     }
50397 });
50398
50399
50400 /**
50401  * @class Roo.form.Row
50402  * @extends Roo.form.Layout
50403  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
50404  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50405  * @constructor
50406  * @param {Object} config Configuration options
50407  */
50408
50409  
50410 Roo.form.Row = function(config){
50411     Roo.form.Row.superclass.constructor.call(this, config);
50412 };
50413  
50414 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50415       /**
50416      * @cfg {Number/String} width
50417      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50418      */
50419     /**
50420      * @cfg {Number/String} height
50421      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50422      */
50423     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50424     
50425     padWidth : 20,
50426     // private
50427     onRender : function(ct, position){
50428         //console.log('row render');
50429         if(!this.rowTpl){
50430             var t = new Roo.Template(
50431                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50432                     '<label for="{0}" style="{2}">{1}{4}</label>',
50433                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50434                     '</div>',
50435                 '</div>'
50436             );
50437             t.disableFormats = true;
50438             t.compile();
50439             Roo.form.Layout.prototype.rowTpl = t;
50440         }
50441         this.fieldTpl = this.rowTpl;
50442         
50443         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50444         var labelWidth = 100;
50445         
50446         if ((this.labelAlign != 'top')) {
50447             if (typeof this.labelWidth == 'number') {
50448                 labelWidth = this.labelWidth
50449             }
50450             this.padWidth =  20 + labelWidth;
50451             
50452         }
50453         
50454         Roo.form.Column.superclass.onRender.call(this, ct, position);
50455         if(this.width){
50456             this.el.setWidth(this.width);
50457         }
50458         if(this.height){
50459             this.el.setHeight(this.height);
50460         }
50461     },
50462     
50463     // private
50464     renderField : function(f){
50465         f.fieldEl = this.fieldTpl.append(this.el, [
50466                f.id, f.fieldLabel,
50467                f.labelStyle||this.labelStyle||'',
50468                this.elementStyle||'',
50469                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50470                f.itemCls||this.itemCls||'',
50471                f.width ? f.width + this.padWidth : 160 + this.padWidth
50472        ],true);
50473     }
50474 });
50475  
50476
50477 /**
50478  * @class Roo.form.FieldSet
50479  * @extends Roo.form.Layout
50480  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50481  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50482  * @constructor
50483  * @param {Object} config Configuration options
50484  */
50485 Roo.form.FieldSet = function(config){
50486     Roo.form.FieldSet.superclass.constructor.call(this, config);
50487 };
50488
50489 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50490     /**
50491      * @cfg {String} legend
50492      * The text to display as the legend for the FieldSet (defaults to '')
50493      */
50494     /**
50495      * @cfg {String/Object} autoCreate
50496      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50497      */
50498
50499     // private
50500     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50501
50502     // private
50503     onRender : function(ct, position){
50504         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50505         if(this.legend){
50506             this.setLegend(this.legend);
50507         }
50508     },
50509
50510     // private
50511     setLegend : function(text){
50512         if(this.rendered){
50513             this.el.child('legend').update(text);
50514         }
50515     }
50516 });/*
50517  * Based on:
50518  * Ext JS Library 1.1.1
50519  * Copyright(c) 2006-2007, Ext JS, LLC.
50520  *
50521  * Originally Released Under LGPL - original licence link has changed is not relivant.
50522  *
50523  * Fork - LGPL
50524  * <script type="text/javascript">
50525  */
50526 /**
50527  * @class Roo.form.VTypes
50528  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50529  * @static
50530  */
50531 Roo.form.VTypes = function(){
50532     // closure these in so they are only created once.
50533     var alpha = /^[a-zA-Z_]+$/;
50534     var alphanum = /^[a-zA-Z0-9_]+$/;
50535     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50536     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50537
50538     // All these messages and functions are configurable
50539     return {
50540         /**
50541          * The function used to validate email addresses
50542          * @param {String} value The email address
50543          */
50544         'email' : function(v){
50545             return email.test(v);
50546         },
50547         /**
50548          * The error text to display when the email validation function returns false
50549          * @type String
50550          */
50551         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50552         /**
50553          * The keystroke filter mask to be applied on email input
50554          * @type RegExp
50555          */
50556         'emailMask' : /[a-z0-9_\.\-@]/i,
50557
50558         /**
50559          * The function used to validate URLs
50560          * @param {String} value The URL
50561          */
50562         'url' : function(v){
50563             return url.test(v);
50564         },
50565         /**
50566          * The error text to display when the url validation function returns false
50567          * @type String
50568          */
50569         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50570         
50571         /**
50572          * The function used to validate alpha values
50573          * @param {String} value The value
50574          */
50575         'alpha' : function(v){
50576             return alpha.test(v);
50577         },
50578         /**
50579          * The error text to display when the alpha validation function returns false
50580          * @type String
50581          */
50582         'alphaText' : 'This field should only contain letters and _',
50583         /**
50584          * The keystroke filter mask to be applied on alpha input
50585          * @type RegExp
50586          */
50587         'alphaMask' : /[a-z_]/i,
50588
50589         /**
50590          * The function used to validate alphanumeric values
50591          * @param {String} value The value
50592          */
50593         'alphanum' : function(v){
50594             return alphanum.test(v);
50595         },
50596         /**
50597          * The error text to display when the alphanumeric validation function returns false
50598          * @type String
50599          */
50600         'alphanumText' : 'This field should only contain letters, numbers and _',
50601         /**
50602          * The keystroke filter mask to be applied on alphanumeric input
50603          * @type RegExp
50604          */
50605         'alphanumMask' : /[a-z0-9_]/i
50606     };
50607 }();//<script type="text/javascript">
50608
50609 /**
50610  * @class Roo.form.FCKeditor
50611  * @extends Roo.form.TextArea
50612  * Wrapper around the FCKEditor http://www.fckeditor.net
50613  * @constructor
50614  * Creates a new FCKeditor
50615  * @param {Object} config Configuration options
50616  */
50617 Roo.form.FCKeditor = function(config){
50618     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50619     this.addEvents({
50620          /**
50621          * @event editorinit
50622          * Fired when the editor is initialized - you can add extra handlers here..
50623          * @param {FCKeditor} this
50624          * @param {Object} the FCK object.
50625          */
50626         editorinit : true
50627     });
50628     
50629     
50630 };
50631 Roo.form.FCKeditor.editors = { };
50632 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
50633 {
50634     //defaultAutoCreate : {
50635     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
50636     //},
50637     // private
50638     /**
50639      * @cfg {Object} fck options - see fck manual for details.
50640      */
50641     fckconfig : false,
50642     
50643     /**
50644      * @cfg {Object} fck toolbar set (Basic or Default)
50645      */
50646     toolbarSet : 'Basic',
50647     /**
50648      * @cfg {Object} fck BasePath
50649      */ 
50650     basePath : '/fckeditor/',
50651     
50652     
50653     frame : false,
50654     
50655     value : '',
50656     
50657    
50658     onRender : function(ct, position)
50659     {
50660         if(!this.el){
50661             this.defaultAutoCreate = {
50662                 tag: "textarea",
50663                 style:"width:300px;height:60px;",
50664                 autocomplete: "new-password"
50665             };
50666         }
50667         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
50668         /*
50669         if(this.grow){
50670             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
50671             if(this.preventScrollbars){
50672                 this.el.setStyle("overflow", "hidden");
50673             }
50674             this.el.setHeight(this.growMin);
50675         }
50676         */
50677         //console.log('onrender' + this.getId() );
50678         Roo.form.FCKeditor.editors[this.getId()] = this;
50679          
50680
50681         this.replaceTextarea() ;
50682         
50683     },
50684     
50685     getEditor : function() {
50686         return this.fckEditor;
50687     },
50688     /**
50689      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
50690      * @param {Mixed} value The value to set
50691      */
50692     
50693     
50694     setValue : function(value)
50695     {
50696         //console.log('setValue: ' + value);
50697         
50698         if(typeof(value) == 'undefined') { // not sure why this is happending...
50699             return;
50700         }
50701         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50702         
50703         //if(!this.el || !this.getEditor()) {
50704         //    this.value = value;
50705             //this.setValue.defer(100,this,[value]);    
50706         //    return;
50707         //} 
50708         
50709         if(!this.getEditor()) {
50710             return;
50711         }
50712         
50713         this.getEditor().SetData(value);
50714         
50715         //
50716
50717     },
50718
50719     /**
50720      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
50721      * @return {Mixed} value The field value
50722      */
50723     getValue : function()
50724     {
50725         
50726         if (this.frame && this.frame.dom.style.display == 'none') {
50727             return Roo.form.FCKeditor.superclass.getValue.call(this);
50728         }
50729         
50730         if(!this.el || !this.getEditor()) {
50731            
50732            // this.getValue.defer(100,this); 
50733             return this.value;
50734         }
50735        
50736         
50737         var value=this.getEditor().GetData();
50738         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50739         return Roo.form.FCKeditor.superclass.getValue.call(this);
50740         
50741
50742     },
50743
50744     /**
50745      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
50746      * @return {Mixed} value The field value
50747      */
50748     getRawValue : function()
50749     {
50750         if (this.frame && this.frame.dom.style.display == 'none') {
50751             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50752         }
50753         
50754         if(!this.el || !this.getEditor()) {
50755             //this.getRawValue.defer(100,this); 
50756             return this.value;
50757             return;
50758         }
50759         
50760         
50761         
50762         var value=this.getEditor().GetData();
50763         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
50764         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
50765          
50766     },
50767     
50768     setSize : function(w,h) {
50769         
50770         
50771         
50772         //if (this.frame && this.frame.dom.style.display == 'none') {
50773         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50774         //    return;
50775         //}
50776         //if(!this.el || !this.getEditor()) {
50777         //    this.setSize.defer(100,this, [w,h]); 
50778         //    return;
50779         //}
50780         
50781         
50782         
50783         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
50784         
50785         this.frame.dom.setAttribute('width', w);
50786         this.frame.dom.setAttribute('height', h);
50787         this.frame.setSize(w,h);
50788         
50789     },
50790     
50791     toggleSourceEdit : function(value) {
50792         
50793       
50794          
50795         this.el.dom.style.display = value ? '' : 'none';
50796         this.frame.dom.style.display = value ?  'none' : '';
50797         
50798     },
50799     
50800     
50801     focus: function(tag)
50802     {
50803         if (this.frame.dom.style.display == 'none') {
50804             return Roo.form.FCKeditor.superclass.focus.call(this);
50805         }
50806         if(!this.el || !this.getEditor()) {
50807             this.focus.defer(100,this, [tag]); 
50808             return;
50809         }
50810         
50811         
50812         
50813         
50814         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
50815         this.getEditor().Focus();
50816         if (tgs.length) {
50817             if (!this.getEditor().Selection.GetSelection()) {
50818                 this.focus.defer(100,this, [tag]); 
50819                 return;
50820             }
50821             
50822             
50823             var r = this.getEditor().EditorDocument.createRange();
50824             r.setStart(tgs[0],0);
50825             r.setEnd(tgs[0],0);
50826             this.getEditor().Selection.GetSelection().removeAllRanges();
50827             this.getEditor().Selection.GetSelection().addRange(r);
50828             this.getEditor().Focus();
50829         }
50830         
50831     },
50832     
50833     
50834     
50835     replaceTextarea : function()
50836     {
50837         if ( document.getElementById( this.getId() + '___Frame' ) ) {
50838             return ;
50839         }
50840         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
50841         //{
50842             // We must check the elements firstly using the Id and then the name.
50843         var oTextarea = document.getElementById( this.getId() );
50844         
50845         var colElementsByName = document.getElementsByName( this.getId() ) ;
50846          
50847         oTextarea.style.display = 'none' ;
50848
50849         if ( oTextarea.tabIndex ) {            
50850             this.TabIndex = oTextarea.tabIndex ;
50851         }
50852         
50853         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
50854         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
50855         this.frame = Roo.get(this.getId() + '___Frame')
50856     },
50857     
50858     _getConfigHtml : function()
50859     {
50860         var sConfig = '' ;
50861
50862         for ( var o in this.fckconfig ) {
50863             sConfig += sConfig.length > 0  ? '&amp;' : '';
50864             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
50865         }
50866
50867         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
50868     },
50869     
50870     
50871     _getIFrameHtml : function()
50872     {
50873         var sFile = 'fckeditor.html' ;
50874         /* no idea what this is about..
50875         try
50876         {
50877             if ( (/fcksource=true/i).test( window.top.location.search ) )
50878                 sFile = 'fckeditor.original.html' ;
50879         }
50880         catch (e) { 
50881         */
50882
50883         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
50884         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
50885         
50886         
50887         var html = '<iframe id="' + this.getId() +
50888             '___Frame" src="' + sLink +
50889             '" width="' + this.width +
50890             '" height="' + this.height + '"' +
50891             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
50892             ' frameborder="0" scrolling="no"></iframe>' ;
50893
50894         return html ;
50895     },
50896     
50897     _insertHtmlBefore : function( html, element )
50898     {
50899         if ( element.insertAdjacentHTML )       {
50900             // IE
50901             element.insertAdjacentHTML( 'beforeBegin', html ) ;
50902         } else { // Gecko
50903             var oRange = document.createRange() ;
50904             oRange.setStartBefore( element ) ;
50905             var oFragment = oRange.createContextualFragment( html );
50906             element.parentNode.insertBefore( oFragment, element ) ;
50907         }
50908     }
50909     
50910     
50911   
50912     
50913     
50914     
50915     
50916
50917 });
50918
50919 //Roo.reg('fckeditor', Roo.form.FCKeditor);
50920
50921 function FCKeditor_OnComplete(editorInstance){
50922     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
50923     f.fckEditor = editorInstance;
50924     //console.log("loaded");
50925     f.fireEvent('editorinit', f, editorInstance);
50926
50927   
50928
50929  
50930
50931
50932
50933
50934
50935
50936
50937
50938
50939
50940
50941
50942
50943
50944
50945 //<script type="text/javascript">
50946 /**
50947  * @class Roo.form.GridField
50948  * @extends Roo.form.Field
50949  * Embed a grid (or editable grid into a form)
50950  * STATUS ALPHA
50951  * 
50952  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50953  * it needs 
50954  * xgrid.store = Roo.data.Store
50955  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50956  * xgrid.store.reader = Roo.data.JsonReader 
50957  * 
50958  * 
50959  * @constructor
50960  * Creates a new GridField
50961  * @param {Object} config Configuration options
50962  */
50963 Roo.form.GridField = function(config){
50964     Roo.form.GridField.superclass.constructor.call(this, config);
50965      
50966 };
50967
50968 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50969     /**
50970      * @cfg {Number} width  - used to restrict width of grid..
50971      */
50972     width : 100,
50973     /**
50974      * @cfg {Number} height - used to restrict height of grid..
50975      */
50976     height : 50,
50977      /**
50978      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50979          * 
50980          *}
50981      */
50982     xgrid : false, 
50983     /**
50984      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50985      * {tag: "input", type: "checkbox", autocomplete: "off"})
50986      */
50987    // defaultAutoCreate : { tag: 'div' },
50988     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50989     /**
50990      * @cfg {String} addTitle Text to include for adding a title.
50991      */
50992     addTitle : false,
50993     //
50994     onResize : function(){
50995         Roo.form.Field.superclass.onResize.apply(this, arguments);
50996     },
50997
50998     initEvents : function(){
50999         // Roo.form.Checkbox.superclass.initEvents.call(this);
51000         // has no events...
51001        
51002     },
51003
51004
51005     getResizeEl : function(){
51006         return this.wrap;
51007     },
51008
51009     getPositionEl : function(){
51010         return this.wrap;
51011     },
51012
51013     // private
51014     onRender : function(ct, position){
51015         
51016         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51017         var style = this.style;
51018         delete this.style;
51019         
51020         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51021         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51022         this.viewEl = this.wrap.createChild({ tag: 'div' });
51023         if (style) {
51024             this.viewEl.applyStyles(style);
51025         }
51026         if (this.width) {
51027             this.viewEl.setWidth(this.width);
51028         }
51029         if (this.height) {
51030             this.viewEl.setHeight(this.height);
51031         }
51032         //if(this.inputValue !== undefined){
51033         //this.setValue(this.value);
51034         
51035         
51036         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51037         
51038         
51039         this.grid.render();
51040         this.grid.getDataSource().on('remove', this.refreshValue, this);
51041         this.grid.getDataSource().on('update', this.refreshValue, this);
51042         this.grid.on('afteredit', this.refreshValue, this);
51043  
51044     },
51045      
51046     
51047     /**
51048      * Sets the value of the item. 
51049      * @param {String} either an object  or a string..
51050      */
51051     setValue : function(v){
51052         //this.value = v;
51053         v = v || []; // empty set..
51054         // this does not seem smart - it really only affects memoryproxy grids..
51055         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51056             var ds = this.grid.getDataSource();
51057             // assumes a json reader..
51058             var data = {}
51059             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51060             ds.loadData( data);
51061         }
51062         // clear selection so it does not get stale.
51063         if (this.grid.sm) { 
51064             this.grid.sm.clearSelections();
51065         }
51066         
51067         Roo.form.GridField.superclass.setValue.call(this, v);
51068         this.refreshValue();
51069         // should load data in the grid really....
51070     },
51071     
51072     // private
51073     refreshValue: function() {
51074          var val = [];
51075         this.grid.getDataSource().each(function(r) {
51076             val.push(r.data);
51077         });
51078         this.el.dom.value = Roo.encode(val);
51079     }
51080     
51081      
51082     
51083     
51084 });/*
51085  * Based on:
51086  * Ext JS Library 1.1.1
51087  * Copyright(c) 2006-2007, Ext JS, LLC.
51088  *
51089  * Originally Released Under LGPL - original licence link has changed is not relivant.
51090  *
51091  * Fork - LGPL
51092  * <script type="text/javascript">
51093  */
51094 /**
51095  * @class Roo.form.DisplayField
51096  * @extends Roo.form.Field
51097  * A generic Field to display non-editable data.
51098  * @cfg {Boolean} closable (true|false) default false
51099  * @constructor
51100  * Creates a new Display Field item.
51101  * @param {Object} config Configuration options
51102  */
51103 Roo.form.DisplayField = function(config){
51104     Roo.form.DisplayField.superclass.constructor.call(this, config);
51105     
51106     this.addEvents({
51107         /**
51108          * @event close
51109          * Fires after the click the close btn
51110              * @param {Roo.form.DisplayField} this
51111              */
51112         close : true
51113     });
51114 };
51115
51116 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51117     inputType:      'hidden',
51118     allowBlank:     true,
51119     readOnly:         true,
51120     
51121  
51122     /**
51123      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51124      */
51125     focusClass : undefined,
51126     /**
51127      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51128      */
51129     fieldClass: 'x-form-field',
51130     
51131      /**
51132      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51133      */
51134     valueRenderer: undefined,
51135     
51136     width: 100,
51137     /**
51138      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51139      * {tag: "input", type: "checkbox", autocomplete: "off"})
51140      */
51141      
51142  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51143  
51144     closable : false,
51145     
51146     onResize : function(){
51147         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51148         
51149     },
51150
51151     initEvents : function(){
51152         // Roo.form.Checkbox.superclass.initEvents.call(this);
51153         // has no events...
51154         
51155         if(this.closable){
51156             this.closeEl.on('click', this.onClose, this);
51157         }
51158        
51159     },
51160
51161
51162     getResizeEl : function(){
51163         return this.wrap;
51164     },
51165
51166     getPositionEl : function(){
51167         return this.wrap;
51168     },
51169
51170     // private
51171     onRender : function(ct, position){
51172         
51173         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51174         //if(this.inputValue !== undefined){
51175         this.wrap = this.el.wrap();
51176         
51177         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51178         
51179         if(this.closable){
51180             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51181         }
51182         
51183         if (this.bodyStyle) {
51184             this.viewEl.applyStyles(this.bodyStyle);
51185         }
51186         //this.viewEl.setStyle('padding', '2px');
51187         
51188         this.setValue(this.value);
51189         
51190     },
51191 /*
51192     // private
51193     initValue : Roo.emptyFn,
51194
51195   */
51196
51197         // private
51198     onClick : function(){
51199         
51200     },
51201
51202     /**
51203      * Sets the checked state of the checkbox.
51204      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51205      */
51206     setValue : function(v){
51207         this.value = v;
51208         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51209         // this might be called before we have a dom element..
51210         if (!this.viewEl) {
51211             return;
51212         }
51213         this.viewEl.dom.innerHTML = html;
51214         Roo.form.DisplayField.superclass.setValue.call(this, v);
51215
51216     },
51217     
51218     onClose : function(e)
51219     {
51220         e.preventDefault();
51221         
51222         this.fireEvent('close', this);
51223     }
51224 });/*
51225  * 
51226  * Licence- LGPL
51227  * 
51228  */
51229
51230 /**
51231  * @class Roo.form.DayPicker
51232  * @extends Roo.form.Field
51233  * A Day picker show [M] [T] [W] ....
51234  * @constructor
51235  * Creates a new Day Picker
51236  * @param {Object} config Configuration options
51237  */
51238 Roo.form.DayPicker= function(config){
51239     Roo.form.DayPicker.superclass.constructor.call(this, config);
51240      
51241 };
51242
51243 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51244     /**
51245      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51246      */
51247     focusClass : undefined,
51248     /**
51249      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51250      */
51251     fieldClass: "x-form-field",
51252    
51253     /**
51254      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51255      * {tag: "input", type: "checkbox", autocomplete: "off"})
51256      */
51257     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51258     
51259    
51260     actionMode : 'viewEl', 
51261     //
51262     // private
51263  
51264     inputType : 'hidden',
51265     
51266      
51267     inputElement: false, // real input element?
51268     basedOn: false, // ????
51269     
51270     isFormField: true, // not sure where this is needed!!!!
51271
51272     onResize : function(){
51273         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51274         if(!this.boxLabel){
51275             this.el.alignTo(this.wrap, 'c-c');
51276         }
51277     },
51278
51279     initEvents : function(){
51280         Roo.form.Checkbox.superclass.initEvents.call(this);
51281         this.el.on("click", this.onClick,  this);
51282         this.el.on("change", this.onClick,  this);
51283     },
51284
51285
51286     getResizeEl : function(){
51287         return this.wrap;
51288     },
51289
51290     getPositionEl : function(){
51291         return this.wrap;
51292     },
51293
51294     
51295     // private
51296     onRender : function(ct, position){
51297         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51298        
51299         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51300         
51301         var r1 = '<table><tr>';
51302         var r2 = '<tr class="x-form-daypick-icons">';
51303         for (var i=0; i < 7; i++) {
51304             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51305             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51306         }
51307         
51308         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51309         viewEl.select('img').on('click', this.onClick, this);
51310         this.viewEl = viewEl;   
51311         
51312         
51313         // this will not work on Chrome!!!
51314         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51315         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51316         
51317         
51318           
51319
51320     },
51321
51322     // private
51323     initValue : Roo.emptyFn,
51324
51325     /**
51326      * Returns the checked state of the checkbox.
51327      * @return {Boolean} True if checked, else false
51328      */
51329     getValue : function(){
51330         return this.el.dom.value;
51331         
51332     },
51333
51334         // private
51335     onClick : function(e){ 
51336         //this.setChecked(!this.checked);
51337         Roo.get(e.target).toggleClass('x-menu-item-checked');
51338         this.refreshValue();
51339         //if(this.el.dom.checked != this.checked){
51340         //    this.setValue(this.el.dom.checked);
51341        // }
51342     },
51343     
51344     // private
51345     refreshValue : function()
51346     {
51347         var val = '';
51348         this.viewEl.select('img',true).each(function(e,i,n)  {
51349             val += e.is(".x-menu-item-checked") ? String(n) : '';
51350         });
51351         this.setValue(val, true);
51352     },
51353
51354     /**
51355      * Sets the checked state of the checkbox.
51356      * On is always based on a string comparison between inputValue and the param.
51357      * @param {Boolean/String} value - the value to set 
51358      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51359      */
51360     setValue : function(v,suppressEvent){
51361         if (!this.el.dom) {
51362             return;
51363         }
51364         var old = this.el.dom.value ;
51365         this.el.dom.value = v;
51366         if (suppressEvent) {
51367             return ;
51368         }
51369          
51370         // update display..
51371         this.viewEl.select('img',true).each(function(e,i,n)  {
51372             
51373             var on = e.is(".x-menu-item-checked");
51374             var newv = v.indexOf(String(n)) > -1;
51375             if (on != newv) {
51376                 e.toggleClass('x-menu-item-checked');
51377             }
51378             
51379         });
51380         
51381         
51382         this.fireEvent('change', this, v, old);
51383         
51384         
51385     },
51386    
51387     // handle setting of hidden value by some other method!!?!?
51388     setFromHidden: function()
51389     {
51390         if(!this.el){
51391             return;
51392         }
51393         //console.log("SET FROM HIDDEN");
51394         //alert('setFrom hidden');
51395         this.setValue(this.el.dom.value);
51396     },
51397     
51398     onDestroy : function()
51399     {
51400         if(this.viewEl){
51401             Roo.get(this.viewEl).remove();
51402         }
51403          
51404         Roo.form.DayPicker.superclass.onDestroy.call(this);
51405     }
51406
51407 });/*
51408  * RooJS Library 1.1.1
51409  * Copyright(c) 2008-2011  Alan Knowles
51410  *
51411  * License - LGPL
51412  */
51413  
51414
51415 /**
51416  * @class Roo.form.ComboCheck
51417  * @extends Roo.form.ComboBox
51418  * A combobox for multiple select items.
51419  *
51420  * FIXME - could do with a reset button..
51421  * 
51422  * @constructor
51423  * Create a new ComboCheck
51424  * @param {Object} config Configuration options
51425  */
51426 Roo.form.ComboCheck = function(config){
51427     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51428     // should verify some data...
51429     // like
51430     // hiddenName = required..
51431     // displayField = required
51432     // valudField == required
51433     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51434     var _t = this;
51435     Roo.each(req, function(e) {
51436         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51437             throw "Roo.form.ComboCheck : missing value for: " + e;
51438         }
51439     });
51440     
51441     
51442 };
51443
51444 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51445      
51446      
51447     editable : false,
51448      
51449     selectedClass: 'x-menu-item-checked', 
51450     
51451     // private
51452     onRender : function(ct, position){
51453         var _t = this;
51454         
51455         
51456         
51457         if(!this.tpl){
51458             var cls = 'x-combo-list';
51459
51460             
51461             this.tpl =  new Roo.Template({
51462                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51463                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51464                    '<span>{' + this.displayField + '}</span>' +
51465                     '</div>' 
51466                 
51467             });
51468         }
51469  
51470         
51471         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51472         this.view.singleSelect = false;
51473         this.view.multiSelect = true;
51474         this.view.toggleSelect = true;
51475         this.pageTb.add(new Roo.Toolbar.Fill(), {
51476             
51477             text: 'Done',
51478             handler: function()
51479             {
51480                 _t.collapse();
51481             }
51482         });
51483     },
51484     
51485     onViewOver : function(e, t){
51486         // do nothing...
51487         return;
51488         
51489     },
51490     
51491     onViewClick : function(doFocus,index){
51492         return;
51493         
51494     },
51495     select: function () {
51496         //Roo.log("SELECT CALLED");
51497     },
51498      
51499     selectByValue : function(xv, scrollIntoView){
51500         var ar = this.getValueArray();
51501         var sels = [];
51502         
51503         Roo.each(ar, function(v) {
51504             if(v === undefined || v === null){
51505                 return;
51506             }
51507             var r = this.findRecord(this.valueField, v);
51508             if(r){
51509                 sels.push(this.store.indexOf(r))
51510                 
51511             }
51512         },this);
51513         this.view.select(sels);
51514         return false;
51515     },
51516     
51517     
51518     
51519     onSelect : function(record, index){
51520        // Roo.log("onselect Called");
51521        // this is only called by the clear button now..
51522         this.view.clearSelections();
51523         this.setValue('[]');
51524         if (this.value != this.valueBefore) {
51525             this.fireEvent('change', this, this.value, this.valueBefore);
51526             this.valueBefore = this.value;
51527         }
51528     },
51529     getValueArray : function()
51530     {
51531         var ar = [] ;
51532         
51533         try {
51534             //Roo.log(this.value);
51535             if (typeof(this.value) == 'undefined') {
51536                 return [];
51537             }
51538             var ar = Roo.decode(this.value);
51539             return  ar instanceof Array ? ar : []; //?? valid?
51540             
51541         } catch(e) {
51542             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51543             return [];
51544         }
51545          
51546     },
51547     expand : function ()
51548     {
51549         
51550         Roo.form.ComboCheck.superclass.expand.call(this);
51551         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51552         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51553         
51554
51555     },
51556     
51557     collapse : function(){
51558         Roo.form.ComboCheck.superclass.collapse.call(this);
51559         var sl = this.view.getSelectedIndexes();
51560         var st = this.store;
51561         var nv = [];
51562         var tv = [];
51563         var r;
51564         Roo.each(sl, function(i) {
51565             r = st.getAt(i);
51566             nv.push(r.get(this.valueField));
51567         },this);
51568         this.setValue(Roo.encode(nv));
51569         if (this.value != this.valueBefore) {
51570
51571             this.fireEvent('change', this, this.value, this.valueBefore);
51572             this.valueBefore = this.value;
51573         }
51574         
51575     },
51576     
51577     setValue : function(v){
51578         // Roo.log(v);
51579         this.value = v;
51580         
51581         var vals = this.getValueArray();
51582         var tv = [];
51583         Roo.each(vals, function(k) {
51584             var r = this.findRecord(this.valueField, k);
51585             if(r){
51586                 tv.push(r.data[this.displayField]);
51587             }else if(this.valueNotFoundText !== undefined){
51588                 tv.push( this.valueNotFoundText );
51589             }
51590         },this);
51591        // Roo.log(tv);
51592         
51593         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51594         this.hiddenField.value = v;
51595         this.value = v;
51596     }
51597     
51598 });/*
51599  * Based on:
51600  * Ext JS Library 1.1.1
51601  * Copyright(c) 2006-2007, Ext JS, LLC.
51602  *
51603  * Originally Released Under LGPL - original licence link has changed is not relivant.
51604  *
51605  * Fork - LGPL
51606  * <script type="text/javascript">
51607  */
51608  
51609 /**
51610  * @class Roo.form.Signature
51611  * @extends Roo.form.Field
51612  * Signature field.  
51613  * @constructor
51614  * 
51615  * @param {Object} config Configuration options
51616  */
51617
51618 Roo.form.Signature = function(config){
51619     Roo.form.Signature.superclass.constructor.call(this, config);
51620     
51621     this.addEvents({// not in used??
51622          /**
51623          * @event confirm
51624          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
51625              * @param {Roo.form.Signature} combo This combo box
51626              */
51627         'confirm' : true,
51628         /**
51629          * @event reset
51630          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
51631              * @param {Roo.form.ComboBox} combo This combo box
51632              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
51633              */
51634         'reset' : true
51635     });
51636 };
51637
51638 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
51639     /**
51640      * @cfg {Object} labels Label to use when rendering a form.
51641      * defaults to 
51642      * labels : { 
51643      *      clear : "Clear",
51644      *      confirm : "Confirm"
51645      *  }
51646      */
51647     labels : { 
51648         clear : "Clear",
51649         confirm : "Confirm"
51650     },
51651     /**
51652      * @cfg {Number} width The signature panel width (defaults to 300)
51653      */
51654     width: 300,
51655     /**
51656      * @cfg {Number} height The signature panel height (defaults to 100)
51657      */
51658     height : 100,
51659     /**
51660      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
51661      */
51662     allowBlank : false,
51663     
51664     //private
51665     // {Object} signPanel The signature SVG panel element (defaults to {})
51666     signPanel : {},
51667     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
51668     isMouseDown : false,
51669     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
51670     isConfirmed : false,
51671     // {String} signatureTmp SVG mapping string (defaults to empty string)
51672     signatureTmp : '',
51673     
51674     
51675     defaultAutoCreate : { // modified by initCompnoent..
51676         tag: "input",
51677         type:"hidden"
51678     },
51679
51680     // private
51681     onRender : function(ct, position){
51682         
51683         Roo.form.Signature.superclass.onRender.call(this, ct, position);
51684         
51685         this.wrap = this.el.wrap({
51686             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
51687         });
51688         
51689         this.createToolbar(this);
51690         this.signPanel = this.wrap.createChild({
51691                 tag: 'div',
51692                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
51693             }, this.el
51694         );
51695             
51696         this.svgID = Roo.id();
51697         this.svgEl = this.signPanel.createChild({
51698               xmlns : 'http://www.w3.org/2000/svg',
51699               tag : 'svg',
51700               id : this.svgID + "-svg",
51701               width: this.width,
51702               height: this.height,
51703               viewBox: '0 0 '+this.width+' '+this.height,
51704               cn : [
51705                 {
51706                     tag: "rect",
51707                     id: this.svgID + "-svg-r",
51708                     width: this.width,
51709                     height: this.height,
51710                     fill: "#ffa"
51711                 },
51712                 {
51713                     tag: "line",
51714                     id: this.svgID + "-svg-l",
51715                     x1: "0", // start
51716                     y1: (this.height*0.8), // start set the line in 80% of height
51717                     x2: this.width, // end
51718                     y2: (this.height*0.8), // end set the line in 80% of height
51719                     'stroke': "#666",
51720                     'stroke-width': "1",
51721                     'stroke-dasharray': "3",
51722                     'shape-rendering': "crispEdges",
51723                     'pointer-events': "none"
51724                 },
51725                 {
51726                     tag: "path",
51727                     id: this.svgID + "-svg-p",
51728                     'stroke': "navy",
51729                     'stroke-width': "3",
51730                     'fill': "none",
51731                     'pointer-events': 'none'
51732                 }
51733               ]
51734         });
51735         this.createSVG();
51736         this.svgBox = this.svgEl.dom.getScreenCTM();
51737     },
51738     createSVG : function(){ 
51739         var svg = this.signPanel;
51740         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
51741         var t = this;
51742
51743         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
51744         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
51745         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
51746         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
51747         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
51748         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
51749         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
51750         
51751     },
51752     isTouchEvent : function(e){
51753         return e.type.match(/^touch/);
51754     },
51755     getCoords : function (e) {
51756         var pt    = this.svgEl.dom.createSVGPoint();
51757         pt.x = e.clientX; 
51758         pt.y = e.clientY;
51759         if (this.isTouchEvent(e)) {
51760             pt.x =  e.targetTouches[0].clientX;
51761             pt.y = e.targetTouches[0].clientY;
51762         }
51763         var a = this.svgEl.dom.getScreenCTM();
51764         var b = a.inverse();
51765         var mx = pt.matrixTransform(b);
51766         return mx.x + ',' + mx.y;
51767     },
51768     //mouse event headler 
51769     down : function (e) {
51770         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
51771         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
51772         
51773         this.isMouseDown = true;
51774         
51775         e.preventDefault();
51776     },
51777     move : function (e) {
51778         if (this.isMouseDown) {
51779             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
51780             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
51781         }
51782         
51783         e.preventDefault();
51784     },
51785     up : function (e) {
51786         this.isMouseDown = false;
51787         var sp = this.signatureTmp.split(' ');
51788         
51789         if(sp.length > 1){
51790             if(!sp[sp.length-2].match(/^L/)){
51791                 sp.pop();
51792                 sp.pop();
51793                 sp.push("");
51794                 this.signatureTmp = sp.join(" ");
51795             }
51796         }
51797         if(this.getValue() != this.signatureTmp){
51798             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51799             this.isConfirmed = false;
51800         }
51801         e.preventDefault();
51802     },
51803     
51804     /**
51805      * Protected method that will not generally be called directly. It
51806      * is called when the editor creates its toolbar. Override this method if you need to
51807      * add custom toolbar buttons.
51808      * @param {HtmlEditor} editor
51809      */
51810     createToolbar : function(editor){
51811          function btn(id, toggle, handler){
51812             var xid = fid + '-'+ id ;
51813             return {
51814                 id : xid,
51815                 cmd : id,
51816                 cls : 'x-btn-icon x-edit-'+id,
51817                 enableToggle:toggle !== false,
51818                 scope: editor, // was editor...
51819                 handler:handler||editor.relayBtnCmd,
51820                 clickEvent:'mousedown',
51821                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51822                 tabIndex:-1
51823             };
51824         }
51825         
51826         
51827         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51828         this.tb = tb;
51829         this.tb.add(
51830            {
51831                 cls : ' x-signature-btn x-signature-'+id,
51832                 scope: editor, // was editor...
51833                 handler: this.reset,
51834                 clickEvent:'mousedown',
51835                 text: this.labels.clear
51836             },
51837             {
51838                  xtype : 'Fill',
51839                  xns: Roo.Toolbar
51840             }, 
51841             {
51842                 cls : '  x-signature-btn x-signature-'+id,
51843                 scope: editor, // was editor...
51844                 handler: this.confirmHandler,
51845                 clickEvent:'mousedown',
51846                 text: this.labels.confirm
51847             }
51848         );
51849     
51850     },
51851     //public
51852     /**
51853      * when user is clicked confirm then show this image.....
51854      * 
51855      * @return {String} Image Data URI
51856      */
51857     getImageDataURI : function(){
51858         var svg = this.svgEl.dom.parentNode.innerHTML;
51859         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
51860         return src; 
51861     },
51862     /**
51863      * 
51864      * @return {Boolean} this.isConfirmed
51865      */
51866     getConfirmed : function(){
51867         return this.isConfirmed;
51868     },
51869     /**
51870      * 
51871      * @return {Number} this.width
51872      */
51873     getWidth : function(){
51874         return this.width;
51875     },
51876     /**
51877      * 
51878      * @return {Number} this.height
51879      */
51880     getHeight : function(){
51881         return this.height;
51882     },
51883     // private
51884     getSignature : function(){
51885         return this.signatureTmp;
51886     },
51887     // private
51888     reset : function(){
51889         this.signatureTmp = '';
51890         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51891         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
51892         this.isConfirmed = false;
51893         Roo.form.Signature.superclass.reset.call(this);
51894     },
51895     setSignature : function(s){
51896         this.signatureTmp = s;
51897         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
51898         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
51899         this.setValue(s);
51900         this.isConfirmed = false;
51901         Roo.form.Signature.superclass.reset.call(this);
51902     }, 
51903     test : function(){
51904 //        Roo.log(this.signPanel.dom.contentWindow.up())
51905     },
51906     //private
51907     setConfirmed : function(){
51908         
51909         
51910         
51911 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
51912     },
51913     // private
51914     confirmHandler : function(){
51915         if(!this.getSignature()){
51916             return;
51917         }
51918         
51919         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
51920         this.setValue(this.getSignature());
51921         this.isConfirmed = true;
51922         
51923         this.fireEvent('confirm', this);
51924     },
51925     // private
51926     // Subclasses should provide the validation implementation by overriding this
51927     validateValue : function(value){
51928         if(this.allowBlank){
51929             return true;
51930         }
51931         
51932         if(this.isConfirmed){
51933             return true;
51934         }
51935         return false;
51936     }
51937 });/*
51938  * Based on:
51939  * Ext JS Library 1.1.1
51940  * Copyright(c) 2006-2007, Ext JS, LLC.
51941  *
51942  * Originally Released Under LGPL - original licence link has changed is not relivant.
51943  *
51944  * Fork - LGPL
51945  * <script type="text/javascript">
51946  */
51947  
51948
51949 /**
51950  * @class Roo.form.ComboBox
51951  * @extends Roo.form.TriggerField
51952  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51953  * @constructor
51954  * Create a new ComboBox.
51955  * @param {Object} config Configuration options
51956  */
51957 Roo.form.Select = function(config){
51958     Roo.form.Select.superclass.constructor.call(this, config);
51959      
51960 };
51961
51962 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51963     /**
51964      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51965      */
51966     /**
51967      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51968      * rendering into an Roo.Editor, defaults to false)
51969      */
51970     /**
51971      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51972      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51973      */
51974     /**
51975      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51976      */
51977     /**
51978      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51979      * the dropdown list (defaults to undefined, with no header element)
51980      */
51981
51982      /**
51983      * @cfg {String/Roo.Template} tpl The template to use to render the output
51984      */
51985      
51986     // private
51987     defaultAutoCreate : {tag: "select"  },
51988     /**
51989      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51990      */
51991     listWidth: undefined,
51992     /**
51993      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51994      * mode = 'remote' or 'text' if mode = 'local')
51995      */
51996     displayField: undefined,
51997     /**
51998      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51999      * mode = 'remote' or 'value' if mode = 'local'). 
52000      * Note: use of a valueField requires the user make a selection
52001      * in order for a value to be mapped.
52002      */
52003     valueField: undefined,
52004     
52005     
52006     /**
52007      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52008      * field's data value (defaults to the underlying DOM element's name)
52009      */
52010     hiddenName: undefined,
52011     /**
52012      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52013      */
52014     listClass: '',
52015     /**
52016      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52017      */
52018     selectedClass: 'x-combo-selected',
52019     /**
52020      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52021      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52022      * which displays a downward arrow icon).
52023      */
52024     triggerClass : 'x-form-arrow-trigger',
52025     /**
52026      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52027      */
52028     shadow:'sides',
52029     /**
52030      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52031      * anchor positions (defaults to 'tl-bl')
52032      */
52033     listAlign: 'tl-bl?',
52034     /**
52035      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52036      */
52037     maxHeight: 300,
52038     /**
52039      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52040      * query specified by the allQuery config option (defaults to 'query')
52041      */
52042     triggerAction: 'query',
52043     /**
52044      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52045      * (defaults to 4, does not apply if editable = false)
52046      */
52047     minChars : 4,
52048     /**
52049      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52050      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52051      */
52052     typeAhead: false,
52053     /**
52054      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52055      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52056      */
52057     queryDelay: 500,
52058     /**
52059      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52060      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52061      */
52062     pageSize: 0,
52063     /**
52064      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52065      * when editable = true (defaults to false)
52066      */
52067     selectOnFocus:false,
52068     /**
52069      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52070      */
52071     queryParam: 'query',
52072     /**
52073      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52074      * when mode = 'remote' (defaults to 'Loading...')
52075      */
52076     loadingText: 'Loading...',
52077     /**
52078      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52079      */
52080     resizable: false,
52081     /**
52082      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52083      */
52084     handleHeight : 8,
52085     /**
52086      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52087      * traditional select (defaults to true)
52088      */
52089     editable: true,
52090     /**
52091      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52092      */
52093     allQuery: '',
52094     /**
52095      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52096      */
52097     mode: 'remote',
52098     /**
52099      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52100      * listWidth has a higher value)
52101      */
52102     minListWidth : 70,
52103     /**
52104      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52105      * allow the user to set arbitrary text into the field (defaults to false)
52106      */
52107     forceSelection:false,
52108     /**
52109      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52110      * if typeAhead = true (defaults to 250)
52111      */
52112     typeAheadDelay : 250,
52113     /**
52114      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52115      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52116      */
52117     valueNotFoundText : undefined,
52118     
52119     /**
52120      * @cfg {String} defaultValue The value displayed after loading the store.
52121      */
52122     defaultValue: '',
52123     
52124     /**
52125      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52126      */
52127     blockFocus : false,
52128     
52129     /**
52130      * @cfg {Boolean} disableClear Disable showing of clear button.
52131      */
52132     disableClear : false,
52133     /**
52134      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52135      */
52136     alwaysQuery : false,
52137     
52138     //private
52139     addicon : false,
52140     editicon: false,
52141     
52142     // element that contains real text value.. (when hidden is used..)
52143      
52144     // private
52145     onRender : function(ct, position){
52146         Roo.form.Field.prototype.onRender.call(this, ct, position);
52147         
52148         if(this.store){
52149             this.store.on('beforeload', this.onBeforeLoad, this);
52150             this.store.on('load', this.onLoad, this);
52151             this.store.on('loadexception', this.onLoadException, this);
52152             this.store.load({});
52153         }
52154         
52155         
52156         
52157     },
52158
52159     // private
52160     initEvents : function(){
52161         //Roo.form.ComboBox.superclass.initEvents.call(this);
52162  
52163     },
52164
52165     onDestroy : function(){
52166        
52167         if(this.store){
52168             this.store.un('beforeload', this.onBeforeLoad, this);
52169             this.store.un('load', this.onLoad, this);
52170             this.store.un('loadexception', this.onLoadException, this);
52171         }
52172         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52173     },
52174
52175     // private
52176     fireKey : function(e){
52177         if(e.isNavKeyPress() && !this.list.isVisible()){
52178             this.fireEvent("specialkey", this, e);
52179         }
52180     },
52181
52182     // private
52183     onResize: function(w, h){
52184         
52185         return; 
52186     
52187         
52188     },
52189
52190     /**
52191      * Allow or prevent the user from directly editing the field text.  If false is passed,
52192      * the user will only be able to select from the items defined in the dropdown list.  This method
52193      * is the runtime equivalent of setting the 'editable' config option at config time.
52194      * @param {Boolean} value True to allow the user to directly edit the field text
52195      */
52196     setEditable : function(value){
52197          
52198     },
52199
52200     // private
52201     onBeforeLoad : function(){
52202         
52203         Roo.log("Select before load");
52204         return;
52205     
52206         this.innerList.update(this.loadingText ?
52207                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52208         //this.restrictHeight();
52209         this.selectedIndex = -1;
52210     },
52211
52212     // private
52213     onLoad : function(){
52214
52215     
52216         var dom = this.el.dom;
52217         dom.innerHTML = '';
52218          var od = dom.ownerDocument;
52219          
52220         if (this.emptyText) {
52221             var op = od.createElement('option');
52222             op.setAttribute('value', '');
52223             op.innerHTML = String.format('{0}', this.emptyText);
52224             dom.appendChild(op);
52225         }
52226         if(this.store.getCount() > 0){
52227            
52228             var vf = this.valueField;
52229             var df = this.displayField;
52230             this.store.data.each(function(r) {
52231                 // which colmsn to use... testing - cdoe / title..
52232                 var op = od.createElement('option');
52233                 op.setAttribute('value', r.data[vf]);
52234                 op.innerHTML = String.format('{0}', r.data[df]);
52235                 dom.appendChild(op);
52236             });
52237             if (typeof(this.defaultValue != 'undefined')) {
52238                 this.setValue(this.defaultValue);
52239             }
52240             
52241              
52242         }else{
52243             //this.onEmptyResults();
52244         }
52245         //this.el.focus();
52246     },
52247     // private
52248     onLoadException : function()
52249     {
52250         dom.innerHTML = '';
52251             
52252         Roo.log("Select on load exception");
52253         return;
52254     
52255         this.collapse();
52256         Roo.log(this.store.reader.jsonData);
52257         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52258             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52259         }
52260         
52261         
52262     },
52263     // private
52264     onTypeAhead : function(){
52265          
52266     },
52267
52268     // private
52269     onSelect : function(record, index){
52270         Roo.log('on select?');
52271         return;
52272         if(this.fireEvent('beforeselect', this, record, index) !== false){
52273             this.setFromData(index > -1 ? record.data : false);
52274             this.collapse();
52275             this.fireEvent('select', this, record, index);
52276         }
52277     },
52278
52279     /**
52280      * Returns the currently selected field value or empty string if no value is set.
52281      * @return {String} value The selected value
52282      */
52283     getValue : function(){
52284         var dom = this.el.dom;
52285         this.value = dom.options[dom.selectedIndex].value;
52286         return this.value;
52287         
52288     },
52289
52290     /**
52291      * Clears any text/value currently set in the field
52292      */
52293     clearValue : function(){
52294         this.value = '';
52295         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52296         
52297     },
52298
52299     /**
52300      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52301      * will be displayed in the field.  If the value does not match the data value of an existing item,
52302      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52303      * Otherwise the field will be blank (although the value will still be set).
52304      * @param {String} value The value to match
52305      */
52306     setValue : function(v){
52307         var d = this.el.dom;
52308         for (var i =0; i < d.options.length;i++) {
52309             if (v == d.options[i].value) {
52310                 d.selectedIndex = i;
52311                 this.value = v;
52312                 return;
52313             }
52314         }
52315         this.clearValue();
52316     },
52317     /**
52318      * @property {Object} the last set data for the element
52319      */
52320     
52321     lastData : false,
52322     /**
52323      * Sets the value of the field based on a object which is related to the record format for the store.
52324      * @param {Object} value the value to set as. or false on reset?
52325      */
52326     setFromData : function(o){
52327         Roo.log('setfrom data?');
52328          
52329         
52330         
52331     },
52332     // private
52333     reset : function(){
52334         this.clearValue();
52335     },
52336     // private
52337     findRecord : function(prop, value){
52338         
52339         return false;
52340     
52341         var record;
52342         if(this.store.getCount() > 0){
52343             this.store.each(function(r){
52344                 if(r.data[prop] == value){
52345                     record = r;
52346                     return false;
52347                 }
52348                 return true;
52349             });
52350         }
52351         return record;
52352     },
52353     
52354     getName: function()
52355     {
52356         // returns hidden if it's set..
52357         if (!this.rendered) {return ''};
52358         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52359         
52360     },
52361      
52362
52363     
52364
52365     // private
52366     onEmptyResults : function(){
52367         Roo.log('empty results');
52368         //this.collapse();
52369     },
52370
52371     /**
52372      * Returns true if the dropdown list is expanded, else false.
52373      */
52374     isExpanded : function(){
52375         return false;
52376     },
52377
52378     /**
52379      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52380      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52381      * @param {String} value The data value of the item to select
52382      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52383      * selected item if it is not currently in view (defaults to true)
52384      * @return {Boolean} True if the value matched an item in the list, else false
52385      */
52386     selectByValue : function(v, scrollIntoView){
52387         Roo.log('select By Value');
52388         return false;
52389     
52390         if(v !== undefined && v !== null){
52391             var r = this.findRecord(this.valueField || this.displayField, v);
52392             if(r){
52393                 this.select(this.store.indexOf(r), scrollIntoView);
52394                 return true;
52395             }
52396         }
52397         return false;
52398     },
52399
52400     /**
52401      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52402      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52403      * @param {Number} index The zero-based index of the list item to select
52404      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52405      * selected item if it is not currently in view (defaults to true)
52406      */
52407     select : function(index, scrollIntoView){
52408         Roo.log('select ');
52409         return  ;
52410         
52411         this.selectedIndex = index;
52412         this.view.select(index);
52413         if(scrollIntoView !== false){
52414             var el = this.view.getNode(index);
52415             if(el){
52416                 this.innerList.scrollChildIntoView(el, false);
52417             }
52418         }
52419     },
52420
52421       
52422
52423     // private
52424     validateBlur : function(){
52425         
52426         return;
52427         
52428     },
52429
52430     // private
52431     initQuery : function(){
52432         this.doQuery(this.getRawValue());
52433     },
52434
52435     // private
52436     doForce : function(){
52437         if(this.el.dom.value.length > 0){
52438             this.el.dom.value =
52439                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52440              
52441         }
52442     },
52443
52444     /**
52445      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52446      * query allowing the query action to be canceled if needed.
52447      * @param {String} query The SQL query to execute
52448      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52449      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52450      * saved in the current store (defaults to false)
52451      */
52452     doQuery : function(q, forceAll){
52453         
52454         Roo.log('doQuery?');
52455         if(q === undefined || q === null){
52456             q = '';
52457         }
52458         var qe = {
52459             query: q,
52460             forceAll: forceAll,
52461             combo: this,
52462             cancel:false
52463         };
52464         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52465             return false;
52466         }
52467         q = qe.query;
52468         forceAll = qe.forceAll;
52469         if(forceAll === true || (q.length >= this.minChars)){
52470             if(this.lastQuery != q || this.alwaysQuery){
52471                 this.lastQuery = q;
52472                 if(this.mode == 'local'){
52473                     this.selectedIndex = -1;
52474                     if(forceAll){
52475                         this.store.clearFilter();
52476                     }else{
52477                         this.store.filter(this.displayField, q);
52478                     }
52479                     this.onLoad();
52480                 }else{
52481                     this.store.baseParams[this.queryParam] = q;
52482                     this.store.load({
52483                         params: this.getParams(q)
52484                     });
52485                     this.expand();
52486                 }
52487             }else{
52488                 this.selectedIndex = -1;
52489                 this.onLoad();   
52490             }
52491         }
52492     },
52493
52494     // private
52495     getParams : function(q){
52496         var p = {};
52497         //p[this.queryParam] = q;
52498         if(this.pageSize){
52499             p.start = 0;
52500             p.limit = this.pageSize;
52501         }
52502         return p;
52503     },
52504
52505     /**
52506      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52507      */
52508     collapse : function(){
52509         
52510     },
52511
52512     // private
52513     collapseIf : function(e){
52514         
52515     },
52516
52517     /**
52518      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52519      */
52520     expand : function(){
52521         
52522     } ,
52523
52524     // private
52525      
52526
52527     /** 
52528     * @cfg {Boolean} grow 
52529     * @hide 
52530     */
52531     /** 
52532     * @cfg {Number} growMin 
52533     * @hide 
52534     */
52535     /** 
52536     * @cfg {Number} growMax 
52537     * @hide 
52538     */
52539     /**
52540      * @hide
52541      * @method autoSize
52542      */
52543     
52544     setWidth : function()
52545     {
52546         
52547     },
52548     getResizeEl : function(){
52549         return this.el;
52550     }
52551 });//<script type="text/javasscript">
52552  
52553
52554 /**
52555  * @class Roo.DDView
52556  * A DnD enabled version of Roo.View.
52557  * @param {Element/String} container The Element in which to create the View.
52558  * @param {String} tpl The template string used to create the markup for each element of the View
52559  * @param {Object} config The configuration properties. These include all the config options of
52560  * {@link Roo.View} plus some specific to this class.<br>
52561  * <p>
52562  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52563  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52564  * <p>
52565  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52566 .x-view-drag-insert-above {
52567         border-top:1px dotted #3366cc;
52568 }
52569 .x-view-drag-insert-below {
52570         border-bottom:1px dotted #3366cc;
52571 }
52572 </code></pre>
52573  * 
52574  */
52575  
52576 Roo.DDView = function(container, tpl, config) {
52577     Roo.DDView.superclass.constructor.apply(this, arguments);
52578     this.getEl().setStyle("outline", "0px none");
52579     this.getEl().unselectable();
52580     if (this.dragGroup) {
52581         this.setDraggable(this.dragGroup.split(","));
52582     }
52583     if (this.dropGroup) {
52584         this.setDroppable(this.dropGroup.split(","));
52585     }
52586     if (this.deletable) {
52587         this.setDeletable();
52588     }
52589     this.isDirtyFlag = false;
52590         this.addEvents({
52591                 "drop" : true
52592         });
52593 };
52594
52595 Roo.extend(Roo.DDView, Roo.View, {
52596 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52597 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52598 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52599 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52600
52601         isFormField: true,
52602
52603         reset: Roo.emptyFn,
52604         
52605         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52606
52607         validate: function() {
52608                 return true;
52609         },
52610         
52611         destroy: function() {
52612                 this.purgeListeners();
52613                 this.getEl.removeAllListeners();
52614                 this.getEl().remove();
52615                 if (this.dragZone) {
52616                         if (this.dragZone.destroy) {
52617                                 this.dragZone.destroy();
52618                         }
52619                 }
52620                 if (this.dropZone) {
52621                         if (this.dropZone.destroy) {
52622                                 this.dropZone.destroy();
52623                         }
52624                 }
52625         },
52626
52627 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
52628         getName: function() {
52629                 return this.name;
52630         },
52631
52632 /**     Loads the View from a JSON string representing the Records to put into the Store. */
52633         setValue: function(v) {
52634                 if (!this.store) {
52635                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
52636                 }
52637                 var data = {};
52638                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
52639                 this.store.proxy = new Roo.data.MemoryProxy(data);
52640                 this.store.load();
52641         },
52642
52643 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
52644         getValue: function() {
52645                 var result = '(';
52646                 this.store.each(function(rec) {
52647                         result += rec.id + ',';
52648                 });
52649                 return result.substr(0, result.length - 1) + ')';
52650         },
52651         
52652         getIds: function() {
52653                 var i = 0, result = new Array(this.store.getCount());
52654                 this.store.each(function(rec) {
52655                         result[i++] = rec.id;
52656                 });
52657                 return result;
52658         },
52659         
52660         isDirty: function() {
52661                 return this.isDirtyFlag;
52662         },
52663
52664 /**
52665  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
52666  *      whole Element becomes the target, and this causes the drop gesture to append.
52667  */
52668     getTargetFromEvent : function(e) {
52669                 var target = e.getTarget();
52670                 while ((target !== null) && (target.parentNode != this.el.dom)) {
52671                 target = target.parentNode;
52672                 }
52673                 if (!target) {
52674                         target = this.el.dom.lastChild || this.el.dom;
52675                 }
52676                 return target;
52677     },
52678
52679 /**
52680  *      Create the drag data which consists of an object which has the property "ddel" as
52681  *      the drag proxy element. 
52682  */
52683     getDragData : function(e) {
52684         var target = this.findItemFromChild(e.getTarget());
52685                 if(target) {
52686                         this.handleSelection(e);
52687                         var selNodes = this.getSelectedNodes();
52688             var dragData = {
52689                 source: this,
52690                 copy: this.copy || (this.allowCopy && e.ctrlKey),
52691                 nodes: selNodes,
52692                 records: []
52693                         };
52694                         var selectedIndices = this.getSelectedIndexes();
52695                         for (var i = 0; i < selectedIndices.length; i++) {
52696                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
52697                         }
52698                         if (selNodes.length == 1) {
52699                                 dragData.ddel = target.cloneNode(true); // the div element
52700                         } else {
52701                                 var div = document.createElement('div'); // create the multi element drag "ghost"
52702                                 div.className = 'multi-proxy';
52703                                 for (var i = 0, len = selNodes.length; i < len; i++) {
52704                                         div.appendChild(selNodes[i].cloneNode(true));
52705                                 }
52706                                 dragData.ddel = div;
52707                         }
52708             //console.log(dragData)
52709             //console.log(dragData.ddel.innerHTML)
52710                         return dragData;
52711                 }
52712         //console.log('nodragData')
52713                 return false;
52714     },
52715     
52716 /**     Specify to which ddGroup items in this DDView may be dragged. */
52717     setDraggable: function(ddGroup) {
52718         if (ddGroup instanceof Array) {
52719                 Roo.each(ddGroup, this.setDraggable, this);
52720                 return;
52721         }
52722         if (this.dragZone) {
52723                 this.dragZone.addToGroup(ddGroup);
52724         } else {
52725                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
52726                                 containerScroll: true,
52727                                 ddGroup: ddGroup 
52728
52729                         });
52730 //                      Draggability implies selection. DragZone's mousedown selects the element.
52731                         if (!this.multiSelect) { this.singleSelect = true; }
52732
52733 //                      Wire the DragZone's handlers up to methods in *this*
52734                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
52735                 }
52736     },
52737
52738 /**     Specify from which ddGroup this DDView accepts drops. */
52739     setDroppable: function(ddGroup) {
52740         if (ddGroup instanceof Array) {
52741                 Roo.each(ddGroup, this.setDroppable, this);
52742                 return;
52743         }
52744         if (this.dropZone) {
52745                 this.dropZone.addToGroup(ddGroup);
52746         } else {
52747                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
52748                                 containerScroll: true,
52749                                 ddGroup: ddGroup
52750                         });
52751
52752 //                      Wire the DropZone's handlers up to methods in *this*
52753                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
52754                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
52755                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
52756                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
52757                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
52758                 }
52759     },
52760
52761 /**     Decide whether to drop above or below a View node. */
52762     getDropPoint : function(e, n, dd){
52763         if (n == this.el.dom) { return "above"; }
52764                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
52765                 var c = t + (b - t) / 2;
52766                 var y = Roo.lib.Event.getPageY(e);
52767                 if(y <= c) {
52768                         return "above";
52769                 }else{
52770                         return "below";
52771                 }
52772     },
52773
52774     onNodeEnter : function(n, dd, e, data){
52775                 return false;
52776     },
52777     
52778     onNodeOver : function(n, dd, e, data){
52779                 var pt = this.getDropPoint(e, n, dd);
52780                 // set the insert point style on the target node
52781                 var dragElClass = this.dropNotAllowed;
52782                 if (pt) {
52783                         var targetElClass;
52784                         if (pt == "above"){
52785                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
52786                                 targetElClass = "x-view-drag-insert-above";
52787                         } else {
52788                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
52789                                 targetElClass = "x-view-drag-insert-below";
52790                         }
52791                         if (this.lastInsertClass != targetElClass){
52792                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
52793                                 this.lastInsertClass = targetElClass;
52794                         }
52795                 }
52796                 return dragElClass;
52797         },
52798
52799     onNodeOut : function(n, dd, e, data){
52800                 this.removeDropIndicators(n);
52801     },
52802
52803     onNodeDrop : function(n, dd, e, data){
52804         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
52805                 return false;
52806         }
52807         var pt = this.getDropPoint(e, n, dd);
52808                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
52809                 if (pt == "below") { insertAt++; }
52810                 for (var i = 0; i < data.records.length; i++) {
52811                         var r = data.records[i];
52812                         var dup = this.store.getById(r.id);
52813                         if (dup && (dd != this.dragZone)) {
52814                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
52815                         } else {
52816                                 if (data.copy) {
52817                                         this.store.insert(insertAt++, r.copy());
52818                                 } else {
52819                                         data.source.isDirtyFlag = true;
52820                                         r.store.remove(r);
52821                                         this.store.insert(insertAt++, r);
52822                                 }
52823                                 this.isDirtyFlag = true;
52824                         }
52825                 }
52826                 this.dragZone.cachedTarget = null;
52827                 return true;
52828     },
52829
52830     removeDropIndicators : function(n){
52831                 if(n){
52832                         Roo.fly(n).removeClass([
52833                                 "x-view-drag-insert-above",
52834                                 "x-view-drag-insert-below"]);
52835                         this.lastInsertClass = "_noclass";
52836                 }
52837     },
52838
52839 /**
52840  *      Utility method. Add a delete option to the DDView's context menu.
52841  *      @param {String} imageUrl The URL of the "delete" icon image.
52842  */
52843         setDeletable: function(imageUrl) {
52844                 if (!this.singleSelect && !this.multiSelect) {
52845                         this.singleSelect = true;
52846                 }
52847                 var c = this.getContextMenu();
52848                 this.contextMenu.on("itemclick", function(item) {
52849                         switch (item.id) {
52850                                 case "delete":
52851                                         this.remove(this.getSelectedIndexes());
52852                                         break;
52853                         }
52854                 }, this);
52855                 this.contextMenu.add({
52856                         icon: imageUrl,
52857                         id: "delete",
52858                         text: 'Delete'
52859                 });
52860         },
52861         
52862 /**     Return the context menu for this DDView. */
52863         getContextMenu: function() {
52864                 if (!this.contextMenu) {
52865 //                      Create the View's context menu
52866                         this.contextMenu = new Roo.menu.Menu({
52867                                 id: this.id + "-contextmenu"
52868                         });
52869                         this.el.on("contextmenu", this.showContextMenu, this);
52870                 }
52871                 return this.contextMenu;
52872         },
52873         
52874         disableContextMenu: function() {
52875                 if (this.contextMenu) {
52876                         this.el.un("contextmenu", this.showContextMenu, this);
52877                 }
52878         },
52879
52880         showContextMenu: function(e, item) {
52881         item = this.findItemFromChild(e.getTarget());
52882                 if (item) {
52883                         e.stopEvent();
52884                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
52885                         this.contextMenu.showAt(e.getXY());
52886             }
52887     },
52888
52889 /**
52890  *      Remove {@link Roo.data.Record}s at the specified indices.
52891  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
52892  */
52893     remove: function(selectedIndices) {
52894                 selectedIndices = [].concat(selectedIndices);
52895                 for (var i = 0; i < selectedIndices.length; i++) {
52896                         var rec = this.store.getAt(selectedIndices[i]);
52897                         this.store.remove(rec);
52898                 }
52899     },
52900
52901 /**
52902  *      Double click fires the event, but also, if this is draggable, and there is only one other
52903  *      related DropZone, it transfers the selected node.
52904  */
52905     onDblClick : function(e){
52906         var item = this.findItemFromChild(e.getTarget());
52907         if(item){
52908             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
52909                 return false;
52910             }
52911             if (this.dragGroup) {
52912                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
52913                     while (targets.indexOf(this.dropZone) > -1) {
52914                             targets.remove(this.dropZone);
52915                                 }
52916                     if (targets.length == 1) {
52917                                         this.dragZone.cachedTarget = null;
52918                         var el = Roo.get(targets[0].getEl());
52919                         var box = el.getBox(true);
52920                         targets[0].onNodeDrop(el.dom, {
52921                                 target: el.dom,
52922                                 xy: [box.x, box.y + box.height - 1]
52923                         }, null, this.getDragData(e));
52924                     }
52925                 }
52926         }
52927     },
52928     
52929     handleSelection: function(e) {
52930                 this.dragZone.cachedTarget = null;
52931         var item = this.findItemFromChild(e.getTarget());
52932         if (!item) {
52933                 this.clearSelections(true);
52934                 return;
52935         }
52936                 if (item && (this.multiSelect || this.singleSelect)){
52937                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
52938                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
52939                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
52940                                 this.unselect(item);
52941                         } else {
52942                                 this.select(item, this.multiSelect && e.ctrlKey);
52943                                 this.lastSelection = item;
52944                         }
52945                 }
52946     },
52947
52948     onItemClick : function(item, index, e){
52949                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52950                         return false;
52951                 }
52952                 return true;
52953     },
52954
52955     unselect : function(nodeInfo, suppressEvent){
52956                 var node = this.getNode(nodeInfo);
52957                 if(node && this.isSelected(node)){
52958                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52959                                 Roo.fly(node).removeClass(this.selectedClass);
52960                                 this.selections.remove(node);
52961                                 if(!suppressEvent){
52962                                         this.fireEvent("selectionchange", this, this.selections);
52963                                 }
52964                         }
52965                 }
52966     }
52967 });
52968 /*
52969  * Based on:
52970  * Ext JS Library 1.1.1
52971  * Copyright(c) 2006-2007, Ext JS, LLC.
52972  *
52973  * Originally Released Under LGPL - original licence link has changed is not relivant.
52974  *
52975  * Fork - LGPL
52976  * <script type="text/javascript">
52977  */
52978  
52979 /**
52980  * @class Roo.LayoutManager
52981  * @extends Roo.util.Observable
52982  * Base class for layout managers.
52983  */
52984 Roo.LayoutManager = function(container, config){
52985     Roo.LayoutManager.superclass.constructor.call(this);
52986     this.el = Roo.get(container);
52987     // ie scrollbar fix
52988     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52989         document.body.scroll = "no";
52990     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52991         this.el.position('relative');
52992     }
52993     this.id = this.el.id;
52994     this.el.addClass("x-layout-container");
52995     /** false to disable window resize monitoring @type Boolean */
52996     this.monitorWindowResize = true;
52997     this.regions = {};
52998     this.addEvents({
52999         /**
53000          * @event layout
53001          * Fires when a layout is performed. 
53002          * @param {Roo.LayoutManager} this
53003          */
53004         "layout" : true,
53005         /**
53006          * @event regionresized
53007          * Fires when the user resizes a region. 
53008          * @param {Roo.LayoutRegion} region The resized region
53009          * @param {Number} newSize The new size (width for east/west, height for north/south)
53010          */
53011         "regionresized" : true,
53012         /**
53013          * @event regioncollapsed
53014          * Fires when a region is collapsed. 
53015          * @param {Roo.LayoutRegion} region The collapsed region
53016          */
53017         "regioncollapsed" : true,
53018         /**
53019          * @event regionexpanded
53020          * Fires when a region is expanded.  
53021          * @param {Roo.LayoutRegion} region The expanded region
53022          */
53023         "regionexpanded" : true
53024     });
53025     this.updating = false;
53026     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53027 };
53028
53029 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53030     /**
53031      * Returns true if this layout is currently being updated
53032      * @return {Boolean}
53033      */
53034     isUpdating : function(){
53035         return this.updating; 
53036     },
53037     
53038     /**
53039      * Suspend the LayoutManager from doing auto-layouts while
53040      * making multiple add or remove calls
53041      */
53042     beginUpdate : function(){
53043         this.updating = true;    
53044     },
53045     
53046     /**
53047      * Restore auto-layouts and optionally disable the manager from performing a layout
53048      * @param {Boolean} noLayout true to disable a layout update 
53049      */
53050     endUpdate : function(noLayout){
53051         this.updating = false;
53052         if(!noLayout){
53053             this.layout();
53054         }    
53055     },
53056     
53057     layout: function(){
53058         
53059     },
53060     
53061     onRegionResized : function(region, newSize){
53062         this.fireEvent("regionresized", region, newSize);
53063         this.layout();
53064     },
53065     
53066     onRegionCollapsed : function(region){
53067         this.fireEvent("regioncollapsed", region);
53068     },
53069     
53070     onRegionExpanded : function(region){
53071         this.fireEvent("regionexpanded", region);
53072     },
53073         
53074     /**
53075      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53076      * performs box-model adjustments.
53077      * @return {Object} The size as an object {width: (the width), height: (the height)}
53078      */
53079     getViewSize : function(){
53080         var size;
53081         if(this.el.dom != document.body){
53082             size = this.el.getSize();
53083         }else{
53084             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53085         }
53086         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53087         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53088         return size;
53089     },
53090     
53091     /**
53092      * Returns the Element this layout is bound to.
53093      * @return {Roo.Element}
53094      */
53095     getEl : function(){
53096         return this.el;
53097     },
53098     
53099     /**
53100      * Returns the specified region.
53101      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53102      * @return {Roo.LayoutRegion}
53103      */
53104     getRegion : function(target){
53105         return this.regions[target.toLowerCase()];
53106     },
53107     
53108     onWindowResize : function(){
53109         if(this.monitorWindowResize){
53110             this.layout();
53111         }
53112     }
53113 });/*
53114  * Based on:
53115  * Ext JS Library 1.1.1
53116  * Copyright(c) 2006-2007, Ext JS, LLC.
53117  *
53118  * Originally Released Under LGPL - original licence link has changed is not relivant.
53119  *
53120  * Fork - LGPL
53121  * <script type="text/javascript">
53122  */
53123 /**
53124  * @class Roo.BorderLayout
53125  * @extends Roo.LayoutManager
53126  * @children Roo.ContentPanel
53127  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53128  * please see: <br><br>
53129  * <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>
53130  * <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>
53131  * Example:
53132  <pre><code>
53133  var layout = new Roo.BorderLayout(document.body, {
53134     north: {
53135         initialSize: 25,
53136         titlebar: false
53137     },
53138     west: {
53139         split:true,
53140         initialSize: 200,
53141         minSize: 175,
53142         maxSize: 400,
53143         titlebar: true,
53144         collapsible: true
53145     },
53146     east: {
53147         split:true,
53148         initialSize: 202,
53149         minSize: 175,
53150         maxSize: 400,
53151         titlebar: true,
53152         collapsible: true
53153     },
53154     south: {
53155         split:true,
53156         initialSize: 100,
53157         minSize: 100,
53158         maxSize: 200,
53159         titlebar: true,
53160         collapsible: true
53161     },
53162     center: {
53163         titlebar: true,
53164         autoScroll:true,
53165         resizeTabs: true,
53166         minTabWidth: 50,
53167         preferredTabWidth: 150
53168     }
53169 });
53170
53171 // shorthand
53172 var CP = Roo.ContentPanel;
53173
53174 layout.beginUpdate();
53175 layout.add("north", new CP("north", "North"));
53176 layout.add("south", new CP("south", {title: "South", closable: true}));
53177 layout.add("west", new CP("west", {title: "West"}));
53178 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53179 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53180 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53181 layout.getRegion("center").showPanel("center1");
53182 layout.endUpdate();
53183 </code></pre>
53184
53185 <b>The container the layout is rendered into can be either the body element or any other element.
53186 If it is not the body element, the container needs to either be an absolute positioned element,
53187 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53188 the container size if it is not the body element.</b>
53189
53190 * @constructor
53191 * Create a new BorderLayout
53192 * @param {String/HTMLElement/Element} container The container this layout is bound to
53193 * @param {Object} config Configuration options
53194  */
53195 Roo.BorderLayout = function(container, config){
53196     config = config || {};
53197     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53198     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53199     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53200         var target = this.factory.validRegions[i];
53201         if(config[target]){
53202             this.addRegion(target, config[target]);
53203         }
53204     }
53205 };
53206
53207 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53208         
53209         /**
53210          * @cfg {Roo.LayoutRegion} east
53211          */
53212         /**
53213          * @cfg {Roo.LayoutRegion} west
53214          */
53215         /**
53216          * @cfg {Roo.LayoutRegion} north
53217          */
53218         /**
53219          * @cfg {Roo.LayoutRegion} south
53220          */
53221         /**
53222          * @cfg {Roo.LayoutRegion} center
53223          */
53224     /**
53225      * Creates and adds a new region if it doesn't already exist.
53226      * @param {String} target The target region key (north, south, east, west or center).
53227      * @param {Object} config The regions config object
53228      * @return {BorderLayoutRegion} The new region
53229      */
53230     addRegion : function(target, config){
53231         if(!this.regions[target]){
53232             var r = this.factory.create(target, this, config);
53233             this.bindRegion(target, r);
53234         }
53235         return this.regions[target];
53236     },
53237
53238     // private (kinda)
53239     bindRegion : function(name, r){
53240         this.regions[name] = r;
53241         r.on("visibilitychange", this.layout, this);
53242         r.on("paneladded", this.layout, this);
53243         r.on("panelremoved", this.layout, this);
53244         r.on("invalidated", this.layout, this);
53245         r.on("resized", this.onRegionResized, this);
53246         r.on("collapsed", this.onRegionCollapsed, this);
53247         r.on("expanded", this.onRegionExpanded, this);
53248     },
53249
53250     /**
53251      * Performs a layout update.
53252      */
53253     layout : function(){
53254         if(this.updating) {
53255             return;
53256         }
53257         var size = this.getViewSize();
53258         var w = size.width;
53259         var h = size.height;
53260         var centerW = w;
53261         var centerH = h;
53262         var centerY = 0;
53263         var centerX = 0;
53264         //var x = 0, y = 0;
53265
53266         var rs = this.regions;
53267         var north = rs["north"];
53268         var south = rs["south"]; 
53269         var west = rs["west"];
53270         var east = rs["east"];
53271         var center = rs["center"];
53272         //if(this.hideOnLayout){ // not supported anymore
53273             //c.el.setStyle("display", "none");
53274         //}
53275         if(north && north.isVisible()){
53276             var b = north.getBox();
53277             var m = north.getMargins();
53278             b.width = w - (m.left+m.right);
53279             b.x = m.left;
53280             b.y = m.top;
53281             centerY = b.height + b.y + m.bottom;
53282             centerH -= centerY;
53283             north.updateBox(this.safeBox(b));
53284         }
53285         if(south && south.isVisible()){
53286             var b = south.getBox();
53287             var m = south.getMargins();
53288             b.width = w - (m.left+m.right);
53289             b.x = m.left;
53290             var totalHeight = (b.height + m.top + m.bottom);
53291             b.y = h - totalHeight + m.top;
53292             centerH -= totalHeight;
53293             south.updateBox(this.safeBox(b));
53294         }
53295         if(west && west.isVisible()){
53296             var b = west.getBox();
53297             var m = west.getMargins();
53298             b.height = centerH - (m.top+m.bottom);
53299             b.x = m.left;
53300             b.y = centerY + m.top;
53301             var totalWidth = (b.width + m.left + m.right);
53302             centerX += totalWidth;
53303             centerW -= totalWidth;
53304             west.updateBox(this.safeBox(b));
53305         }
53306         if(east && east.isVisible()){
53307             var b = east.getBox();
53308             var m = east.getMargins();
53309             b.height = centerH - (m.top+m.bottom);
53310             var totalWidth = (b.width + m.left + m.right);
53311             b.x = w - totalWidth + m.left;
53312             b.y = centerY + m.top;
53313             centerW -= totalWidth;
53314             east.updateBox(this.safeBox(b));
53315         }
53316         if(center){
53317             var m = center.getMargins();
53318             var centerBox = {
53319                 x: centerX + m.left,
53320                 y: centerY + m.top,
53321                 width: centerW - (m.left+m.right),
53322                 height: centerH - (m.top+m.bottom)
53323             };
53324             //if(this.hideOnLayout){
53325                 //center.el.setStyle("display", "block");
53326             //}
53327             center.updateBox(this.safeBox(centerBox));
53328         }
53329         this.el.repaint();
53330         this.fireEvent("layout", this);
53331     },
53332
53333     // private
53334     safeBox : function(box){
53335         box.width = Math.max(0, box.width);
53336         box.height = Math.max(0, box.height);
53337         return box;
53338     },
53339
53340     /**
53341      * Adds a ContentPanel (or subclass) to this layout.
53342      * @param {String} target The target region key (north, south, east, west or center).
53343      * @param {Roo.ContentPanel} panel The panel to add
53344      * @return {Roo.ContentPanel} The added panel
53345      */
53346     add : function(target, panel){
53347          
53348         target = target.toLowerCase();
53349         return this.regions[target].add(panel);
53350     },
53351
53352     /**
53353      * Remove a ContentPanel (or subclass) to this layout.
53354      * @param {String} target The target region key (north, south, east, west or center).
53355      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53356      * @return {Roo.ContentPanel} The removed panel
53357      */
53358     remove : function(target, panel){
53359         target = target.toLowerCase();
53360         return this.regions[target].remove(panel);
53361     },
53362
53363     /**
53364      * Searches all regions for a panel with the specified id
53365      * @param {String} panelId
53366      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53367      */
53368     findPanel : function(panelId){
53369         var rs = this.regions;
53370         for(var target in rs){
53371             if(typeof rs[target] != "function"){
53372                 var p = rs[target].getPanel(panelId);
53373                 if(p){
53374                     return p;
53375                 }
53376             }
53377         }
53378         return null;
53379     },
53380
53381     /**
53382      * Searches all regions for a panel with the specified id and activates (shows) it.
53383      * @param {String/ContentPanel} panelId The panels id or the panel itself
53384      * @return {Roo.ContentPanel} The shown panel or null
53385      */
53386     showPanel : function(panelId) {
53387       var rs = this.regions;
53388       for(var target in rs){
53389          var r = rs[target];
53390          if(typeof r != "function"){
53391             if(r.hasPanel(panelId)){
53392                return r.showPanel(panelId);
53393             }
53394          }
53395       }
53396       return null;
53397    },
53398
53399    /**
53400      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53401      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53402      */
53403     restoreState : function(provider){
53404         if(!provider){
53405             provider = Roo.state.Manager;
53406         }
53407         var sm = new Roo.LayoutStateManager();
53408         sm.init(this, provider);
53409     },
53410
53411     /**
53412      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53413      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53414      * a valid ContentPanel config object.  Example:
53415      * <pre><code>
53416 // Create the main layout
53417 var layout = new Roo.BorderLayout('main-ct', {
53418     west: {
53419         split:true,
53420         minSize: 175,
53421         titlebar: true
53422     },
53423     center: {
53424         title:'Components'
53425     }
53426 }, 'main-ct');
53427
53428 // Create and add multiple ContentPanels at once via configs
53429 layout.batchAdd({
53430    west: {
53431        id: 'source-files',
53432        autoCreate:true,
53433        title:'Ext Source Files',
53434        autoScroll:true,
53435        fitToFrame:true
53436    },
53437    center : {
53438        el: cview,
53439        autoScroll:true,
53440        fitToFrame:true,
53441        toolbar: tb,
53442        resizeEl:'cbody'
53443    }
53444 });
53445 </code></pre>
53446      * @param {Object} regions An object containing ContentPanel configs by region name
53447      */
53448     batchAdd : function(regions){
53449         this.beginUpdate();
53450         for(var rname in regions){
53451             var lr = this.regions[rname];
53452             if(lr){
53453                 this.addTypedPanels(lr, regions[rname]);
53454             }
53455         }
53456         this.endUpdate();
53457     },
53458
53459     // private
53460     addTypedPanels : function(lr, ps){
53461         if(typeof ps == 'string'){
53462             lr.add(new Roo.ContentPanel(ps));
53463         }
53464         else if(ps instanceof Array){
53465             for(var i =0, len = ps.length; i < len; i++){
53466                 this.addTypedPanels(lr, ps[i]);
53467             }
53468         }
53469         else if(!ps.events){ // raw config?
53470             var el = ps.el;
53471             delete ps.el; // prevent conflict
53472             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53473         }
53474         else {  // panel object assumed!
53475             lr.add(ps);
53476         }
53477     },
53478     /**
53479      * Adds a xtype elements to the layout.
53480      * <pre><code>
53481
53482 layout.addxtype({
53483        xtype : 'ContentPanel',
53484        region: 'west',
53485        items: [ .... ]
53486    }
53487 );
53488
53489 layout.addxtype({
53490         xtype : 'NestedLayoutPanel',
53491         region: 'west',
53492         layout: {
53493            center: { },
53494            west: { }   
53495         },
53496         items : [ ... list of content panels or nested layout panels.. ]
53497    }
53498 );
53499 </code></pre>
53500      * @param {Object} cfg Xtype definition of item to add.
53501      */
53502     addxtype : function(cfg)
53503     {
53504         // basically accepts a pannel...
53505         // can accept a layout region..!?!?
53506         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53507         
53508         if (!cfg.xtype.match(/Panel$/)) {
53509             return false;
53510         }
53511         var ret = false;
53512         
53513         if (typeof(cfg.region) == 'undefined') {
53514             Roo.log("Failed to add Panel, region was not set");
53515             Roo.log(cfg);
53516             return false;
53517         }
53518         var region = cfg.region;
53519         delete cfg.region;
53520         
53521           
53522         var xitems = [];
53523         if (cfg.items) {
53524             xitems = cfg.items;
53525             delete cfg.items;
53526         }
53527         var nb = false;
53528         
53529         switch(cfg.xtype) 
53530         {
53531             case 'ContentPanel':  // ContentPanel (el, cfg)
53532             case 'ScrollPanel':  // ContentPanel (el, cfg)
53533             case 'ViewPanel': 
53534                 if(cfg.autoCreate) {
53535                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53536                 } else {
53537                     var el = this.el.createChild();
53538                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53539                 }
53540                 
53541                 this.add(region, ret);
53542                 break;
53543             
53544             
53545             case 'TreePanel': // our new panel!
53546                 cfg.el = this.el.createChild();
53547                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53548                 this.add(region, ret);
53549                 break;
53550             
53551             case 'NestedLayoutPanel': 
53552                 // create a new Layout (which is  a Border Layout...
53553                 var el = this.el.createChild();
53554                 var clayout = cfg.layout;
53555                 delete cfg.layout;
53556                 clayout.items   = clayout.items  || [];
53557                 // replace this exitems with the clayout ones..
53558                 xitems = clayout.items;
53559                  
53560                 
53561                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53562                     cfg.background = false;
53563                 }
53564                 var layout = new Roo.BorderLayout(el, clayout);
53565                 
53566                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53567                 //console.log('adding nested layout panel '  + cfg.toSource());
53568                 this.add(region, ret);
53569                 nb = {}; /// find first...
53570                 break;
53571                 
53572             case 'GridPanel': 
53573             
53574                 // needs grid and region
53575                 
53576                 //var el = this.getRegion(region).el.createChild();
53577                 var el = this.el.createChild();
53578                 // create the grid first...
53579                 
53580                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53581                 delete cfg.grid;
53582                 if (region == 'center' && this.active ) {
53583                     cfg.background = false;
53584                 }
53585                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53586                 
53587                 this.add(region, ret);
53588                 if (cfg.background) {
53589                     ret.on('activate', function(gp) {
53590                         if (!gp.grid.rendered) {
53591                             gp.grid.render();
53592                         }
53593                     });
53594                 } else {
53595                     grid.render();
53596                 }
53597                 break;
53598            
53599            
53600            
53601                 
53602                 
53603                 
53604             default:
53605                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53606                     
53607                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53608                     this.add(region, ret);
53609                 } else {
53610                 
53611                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53612                     return null;
53613                 }
53614                 
53615              // GridPanel (grid, cfg)
53616             
53617         }
53618         this.beginUpdate();
53619         // add children..
53620         var region = '';
53621         var abn = {};
53622         Roo.each(xitems, function(i)  {
53623             region = nb && i.region ? i.region : false;
53624             
53625             var add = ret.addxtype(i);
53626            
53627             if (region) {
53628                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
53629                 if (!i.background) {
53630                     abn[region] = nb[region] ;
53631                 }
53632             }
53633             
53634         });
53635         this.endUpdate();
53636
53637         // make the last non-background panel active..
53638         //if (nb) { Roo.log(abn); }
53639         if (nb) {
53640             
53641             for(var r in abn) {
53642                 region = this.getRegion(r);
53643                 if (region) {
53644                     // tried using nb[r], but it does not work..
53645                      
53646                     region.showPanel(abn[r]);
53647                    
53648                 }
53649             }
53650         }
53651         return ret;
53652         
53653     }
53654 });
53655
53656 /**
53657  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
53658  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
53659  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
53660  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
53661  * <pre><code>
53662 // shorthand
53663 var CP = Roo.ContentPanel;
53664
53665 var layout = Roo.BorderLayout.create({
53666     north: {
53667         initialSize: 25,
53668         titlebar: false,
53669         panels: [new CP("north", "North")]
53670     },
53671     west: {
53672         split:true,
53673         initialSize: 200,
53674         minSize: 175,
53675         maxSize: 400,
53676         titlebar: true,
53677         collapsible: true,
53678         panels: [new CP("west", {title: "West"})]
53679     },
53680     east: {
53681         split:true,
53682         initialSize: 202,
53683         minSize: 175,
53684         maxSize: 400,
53685         titlebar: true,
53686         collapsible: true,
53687         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
53688     },
53689     south: {
53690         split:true,
53691         initialSize: 100,
53692         minSize: 100,
53693         maxSize: 200,
53694         titlebar: true,
53695         collapsible: true,
53696         panels: [new CP("south", {title: "South", closable: true})]
53697     },
53698     center: {
53699         titlebar: true,
53700         autoScroll:true,
53701         resizeTabs: true,
53702         minTabWidth: 50,
53703         preferredTabWidth: 150,
53704         panels: [
53705             new CP("center1", {title: "Close Me", closable: true}),
53706             new CP("center2", {title: "Center Panel", closable: false})
53707         ]
53708     }
53709 }, document.body);
53710
53711 layout.getRegion("center").showPanel("center1");
53712 </code></pre>
53713  * @param config
53714  * @param targetEl
53715  */
53716 Roo.BorderLayout.create = function(config, targetEl){
53717     var layout = new Roo.BorderLayout(targetEl || document.body, config);
53718     layout.beginUpdate();
53719     var regions = Roo.BorderLayout.RegionFactory.validRegions;
53720     for(var j = 0, jlen = regions.length; j < jlen; j++){
53721         var lr = regions[j];
53722         if(layout.regions[lr] && config[lr].panels){
53723             var r = layout.regions[lr];
53724             var ps = config[lr].panels;
53725             layout.addTypedPanels(r, ps);
53726         }
53727     }
53728     layout.endUpdate();
53729     return layout;
53730 };
53731
53732 // private
53733 Roo.BorderLayout.RegionFactory = {
53734     // private
53735     validRegions : ["north","south","east","west","center"],
53736
53737     // private
53738     create : function(target, mgr, config){
53739         target = target.toLowerCase();
53740         if(config.lightweight || config.basic){
53741             return new Roo.BasicLayoutRegion(mgr, config, target);
53742         }
53743         switch(target){
53744             case "north":
53745                 return new Roo.NorthLayoutRegion(mgr, config);
53746             case "south":
53747                 return new Roo.SouthLayoutRegion(mgr, config);
53748             case "east":
53749                 return new Roo.EastLayoutRegion(mgr, config);
53750             case "west":
53751                 return new Roo.WestLayoutRegion(mgr, config);
53752             case "center":
53753                 return new Roo.CenterLayoutRegion(mgr, config);
53754         }
53755         throw 'Layout region "'+target+'" not supported.';
53756     }
53757 };/*
53758  * Based on:
53759  * Ext JS Library 1.1.1
53760  * Copyright(c) 2006-2007, Ext JS, LLC.
53761  *
53762  * Originally Released Under LGPL - original licence link has changed is not relivant.
53763  *
53764  * Fork - LGPL
53765  * <script type="text/javascript">
53766  */
53767  
53768 /**
53769  * @class Roo.BasicLayoutRegion
53770  * @extends Roo.util.Observable
53771  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
53772  * and does not have a titlebar, tabs or any other features. All it does is size and position 
53773  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
53774  */
53775 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
53776     this.mgr = mgr;
53777     this.position  = pos;
53778     this.events = {
53779         /**
53780          * @scope Roo.BasicLayoutRegion
53781          */
53782         
53783         /**
53784          * @event beforeremove
53785          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
53786          * @param {Roo.LayoutRegion} this
53787          * @param {Roo.ContentPanel} panel The panel
53788          * @param {Object} e The cancel event object
53789          */
53790         "beforeremove" : true,
53791         /**
53792          * @event invalidated
53793          * Fires when the layout for this region is changed.
53794          * @param {Roo.LayoutRegion} this
53795          */
53796         "invalidated" : true,
53797         /**
53798          * @event visibilitychange
53799          * Fires when this region is shown or hidden 
53800          * @param {Roo.LayoutRegion} this
53801          * @param {Boolean} visibility true or false
53802          */
53803         "visibilitychange" : true,
53804         /**
53805          * @event paneladded
53806          * Fires when a panel is added. 
53807          * @param {Roo.LayoutRegion} this
53808          * @param {Roo.ContentPanel} panel The panel
53809          */
53810         "paneladded" : true,
53811         /**
53812          * @event panelremoved
53813          * Fires when a panel is removed. 
53814          * @param {Roo.LayoutRegion} this
53815          * @param {Roo.ContentPanel} panel The panel
53816          */
53817         "panelremoved" : true,
53818         /**
53819          * @event beforecollapse
53820          * Fires when this region before collapse.
53821          * @param {Roo.LayoutRegion} this
53822          */
53823         "beforecollapse" : true,
53824         /**
53825          * @event collapsed
53826          * Fires when this region is collapsed.
53827          * @param {Roo.LayoutRegion} this
53828          */
53829         "collapsed" : true,
53830         /**
53831          * @event expanded
53832          * Fires when this region is expanded.
53833          * @param {Roo.LayoutRegion} this
53834          */
53835         "expanded" : true,
53836         /**
53837          * @event slideshow
53838          * Fires when this region is slid into view.
53839          * @param {Roo.LayoutRegion} this
53840          */
53841         "slideshow" : true,
53842         /**
53843          * @event slidehide
53844          * Fires when this region slides out of view. 
53845          * @param {Roo.LayoutRegion} this
53846          */
53847         "slidehide" : true,
53848         /**
53849          * @event panelactivated
53850          * Fires when a panel is activated. 
53851          * @param {Roo.LayoutRegion} this
53852          * @param {Roo.ContentPanel} panel The activated panel
53853          */
53854         "panelactivated" : true,
53855         /**
53856          * @event resized
53857          * Fires when the user resizes this region. 
53858          * @param {Roo.LayoutRegion} this
53859          * @param {Number} newSize The new size (width for east/west, height for north/south)
53860          */
53861         "resized" : true
53862     };
53863     /** A collection of panels in this region. @type Roo.util.MixedCollection */
53864     this.panels = new Roo.util.MixedCollection();
53865     this.panels.getKey = this.getPanelId.createDelegate(this);
53866     this.box = null;
53867     this.activePanel = null;
53868     // ensure listeners are added...
53869     
53870     if (config.listeners || config.events) {
53871         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
53872             listeners : config.listeners || {},
53873             events : config.events || {}
53874         });
53875     }
53876     
53877     if(skipConfig !== true){
53878         this.applyConfig(config);
53879     }
53880 };
53881
53882 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
53883     getPanelId : function(p){
53884         return p.getId();
53885     },
53886     
53887     applyConfig : function(config){
53888         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53889         this.config = config;
53890         
53891     },
53892     
53893     /**
53894      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
53895      * the width, for horizontal (north, south) the height.
53896      * @param {Number} newSize The new width or height
53897      */
53898     resizeTo : function(newSize){
53899         var el = this.el ? this.el :
53900                  (this.activePanel ? this.activePanel.getEl() : null);
53901         if(el){
53902             switch(this.position){
53903                 case "east":
53904                 case "west":
53905                     el.setWidth(newSize);
53906                     this.fireEvent("resized", this, newSize);
53907                 break;
53908                 case "north":
53909                 case "south":
53910                     el.setHeight(newSize);
53911                     this.fireEvent("resized", this, newSize);
53912                 break;                
53913             }
53914         }
53915     },
53916     
53917     getBox : function(){
53918         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
53919     },
53920     
53921     getMargins : function(){
53922         return this.margins;
53923     },
53924     
53925     updateBox : function(box){
53926         this.box = box;
53927         var el = this.activePanel.getEl();
53928         el.dom.style.left = box.x + "px";
53929         el.dom.style.top = box.y + "px";
53930         this.activePanel.setSize(box.width, box.height);
53931     },
53932     
53933     /**
53934      * Returns the container element for this region.
53935      * @return {Roo.Element}
53936      */
53937     getEl : function(){
53938         return this.activePanel;
53939     },
53940     
53941     /**
53942      * Returns true if this region is currently visible.
53943      * @return {Boolean}
53944      */
53945     isVisible : function(){
53946         return this.activePanel ? true : false;
53947     },
53948     
53949     setActivePanel : function(panel){
53950         panel = this.getPanel(panel);
53951         if(this.activePanel && this.activePanel != panel){
53952             this.activePanel.setActiveState(false);
53953             this.activePanel.getEl().setLeftTop(-10000,-10000);
53954         }
53955         this.activePanel = panel;
53956         panel.setActiveState(true);
53957         if(this.box){
53958             panel.setSize(this.box.width, this.box.height);
53959         }
53960         this.fireEvent("panelactivated", this, panel);
53961         this.fireEvent("invalidated");
53962     },
53963     
53964     /**
53965      * Show the specified panel.
53966      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53967      * @return {Roo.ContentPanel} The shown panel or null
53968      */
53969     showPanel : function(panel){
53970         if(panel = this.getPanel(panel)){
53971             this.setActivePanel(panel);
53972         }
53973         return panel;
53974     },
53975     
53976     /**
53977      * Get the active panel for this region.
53978      * @return {Roo.ContentPanel} The active panel or null
53979      */
53980     getActivePanel : function(){
53981         return this.activePanel;
53982     },
53983     
53984     /**
53985      * Add the passed ContentPanel(s)
53986      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53987      * @return {Roo.ContentPanel} The panel added (if only one was added)
53988      */
53989     add : function(panel){
53990         if(arguments.length > 1){
53991             for(var i = 0, len = arguments.length; i < len; i++) {
53992                 this.add(arguments[i]);
53993             }
53994             return null;
53995         }
53996         if(this.hasPanel(panel)){
53997             this.showPanel(panel);
53998             return panel;
53999         }
54000         var el = panel.getEl();
54001         if(el.dom.parentNode != this.mgr.el.dom){
54002             this.mgr.el.dom.appendChild(el.dom);
54003         }
54004         if(panel.setRegion){
54005             panel.setRegion(this);
54006         }
54007         this.panels.add(panel);
54008         el.setStyle("position", "absolute");
54009         if(!panel.background){
54010             this.setActivePanel(panel);
54011             if(this.config.initialSize && this.panels.getCount()==1){
54012                 this.resizeTo(this.config.initialSize);
54013             }
54014         }
54015         this.fireEvent("paneladded", this, panel);
54016         return panel;
54017     },
54018     
54019     /**
54020      * Returns true if the panel is in this region.
54021      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54022      * @return {Boolean}
54023      */
54024     hasPanel : function(panel){
54025         if(typeof panel == "object"){ // must be panel obj
54026             panel = panel.getId();
54027         }
54028         return this.getPanel(panel) ? true : false;
54029     },
54030     
54031     /**
54032      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54034      * @param {Boolean} preservePanel Overrides the config preservePanel option
54035      * @return {Roo.ContentPanel} The panel that was removed
54036      */
54037     remove : function(panel, preservePanel){
54038         panel = this.getPanel(panel);
54039         if(!panel){
54040             return null;
54041         }
54042         var e = {};
54043         this.fireEvent("beforeremove", this, panel, e);
54044         if(e.cancel === true){
54045             return null;
54046         }
54047         var panelId = panel.getId();
54048         this.panels.removeKey(panelId);
54049         return panel;
54050     },
54051     
54052     /**
54053      * Returns the panel specified or null if it's not in this region.
54054      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54055      * @return {Roo.ContentPanel}
54056      */
54057     getPanel : function(id){
54058         if(typeof id == "object"){ // must be panel obj
54059             return id;
54060         }
54061         return this.panels.get(id);
54062     },
54063     
54064     /**
54065      * Returns this regions position (north/south/east/west/center).
54066      * @return {String} 
54067      */
54068     getPosition: function(){
54069         return this.position;    
54070     }
54071 });/*
54072  * Based on:
54073  * Ext JS Library 1.1.1
54074  * Copyright(c) 2006-2007, Ext JS, LLC.
54075  *
54076  * Originally Released Under LGPL - original licence link has changed is not relivant.
54077  *
54078  * Fork - LGPL
54079  * <script type="text/javascript">
54080  */
54081  
54082 /**
54083  * @class Roo.LayoutRegion
54084  * @extends Roo.BasicLayoutRegion
54085  * This class represents a region in a layout manager.
54086  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54087  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54088  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54089  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54090  * @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})
54091  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54092  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54093  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54094  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54095  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54096  * @cfg {String}    title           The title for the region (overrides panel titles)
54097  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54098  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54099  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54100  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54101  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54102  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54103  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54104  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54105  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54106  * @cfg {Boolean}   showPin         True to show a pin button
54107  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54108  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54109  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54110  * @cfg {Number}    width           For East/West panels
54111  * @cfg {Number}    height          For North/South panels
54112  * @cfg {Boolean}   split           To show the splitter
54113  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54114  */
54115 Roo.LayoutRegion = function(mgr, config, pos){
54116     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54117     var dh = Roo.DomHelper;
54118     /** This region's container element 
54119     * @type Roo.Element */
54120     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54121     /** This region's title element 
54122     * @type Roo.Element */
54123
54124     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54125         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54126         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54127     ]}, true);
54128     this.titleEl.enableDisplayMode();
54129     /** This region's title text element 
54130     * @type HTMLElement */
54131     this.titleTextEl = this.titleEl.dom.firstChild;
54132     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54133     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54134     this.closeBtn.enableDisplayMode();
54135     this.closeBtn.on("click", this.closeClicked, this);
54136     this.closeBtn.hide();
54137
54138     this.createBody(config);
54139     this.visible = true;
54140     this.collapsed = false;
54141
54142     if(config.hideWhenEmpty){
54143         this.hide();
54144         this.on("paneladded", this.validateVisibility, this);
54145         this.on("panelremoved", this.validateVisibility, this);
54146     }
54147     this.applyConfig(config);
54148 };
54149
54150 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54151
54152     createBody : function(){
54153         /** This region's body element 
54154         * @type Roo.Element */
54155         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54156     },
54157
54158     applyConfig : function(c){
54159         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54160             var dh = Roo.DomHelper;
54161             if(c.titlebar !== false){
54162                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
54163                 this.collapseBtn.on("click", this.collapse, this);
54164                 this.collapseBtn.enableDisplayMode();
54165
54166                 if(c.showPin === true || this.showPin){
54167                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
54168                     this.stickBtn.enableDisplayMode();
54169                     this.stickBtn.on("click", this.expand, this);
54170                     this.stickBtn.hide();
54171                 }
54172             }
54173             /** This region's collapsed element
54174             * @type Roo.Element */
54175             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54176                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54177             ]}, true);
54178             if(c.floatable !== false){
54179                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54180                this.collapsedEl.on("click", this.collapseClick, this);
54181             }
54182
54183             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54184                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54185                    id: "message", unselectable: "on", style:{"float":"left"}});
54186                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54187              }
54188             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54189             this.expandBtn.on("click", this.expand, this);
54190         }
54191         if(this.collapseBtn){
54192             this.collapseBtn.setVisible(c.collapsible == true);
54193         }
54194         this.cmargins = c.cmargins || this.cmargins ||
54195                          (this.position == "west" || this.position == "east" ?
54196                              {top: 0, left: 2, right:2, bottom: 0} :
54197                              {top: 2, left: 0, right:0, bottom: 2});
54198         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54199         this.bottomTabs = c.tabPosition != "top";
54200         this.autoScroll = c.autoScroll || false;
54201         if(this.autoScroll){
54202             this.bodyEl.setStyle("overflow", "auto");
54203         }else{
54204             this.bodyEl.setStyle("overflow", "hidden");
54205         }
54206         //if(c.titlebar !== false){
54207             if((!c.titlebar && !c.title) || c.titlebar === false){
54208                 this.titleEl.hide();
54209             }else{
54210                 this.titleEl.show();
54211                 if(c.title){
54212                     this.titleTextEl.innerHTML = c.title;
54213                 }
54214             }
54215         //}
54216         this.duration = c.duration || .30;
54217         this.slideDuration = c.slideDuration || .45;
54218         this.config = c;
54219         if(c.collapsed){
54220             this.collapse(true);
54221         }
54222         if(c.hidden){
54223             this.hide();
54224         }
54225     },
54226     /**
54227      * Returns true if this region is currently visible.
54228      * @return {Boolean}
54229      */
54230     isVisible : function(){
54231         return this.visible;
54232     },
54233
54234     /**
54235      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54236      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54237      */
54238     setCollapsedTitle : function(title){
54239         title = title || "&#160;";
54240         if(this.collapsedTitleTextEl){
54241             this.collapsedTitleTextEl.innerHTML = title;
54242         }
54243     },
54244
54245     getBox : function(){
54246         var b;
54247         if(!this.collapsed){
54248             b = this.el.getBox(false, true);
54249         }else{
54250             b = this.collapsedEl.getBox(false, true);
54251         }
54252         return b;
54253     },
54254
54255     getMargins : function(){
54256         return this.collapsed ? this.cmargins : this.margins;
54257     },
54258
54259     highlight : function(){
54260         this.el.addClass("x-layout-panel-dragover");
54261     },
54262
54263     unhighlight : function(){
54264         this.el.removeClass("x-layout-panel-dragover");
54265     },
54266
54267     updateBox : function(box){
54268         this.box = box;
54269         if(!this.collapsed){
54270             this.el.dom.style.left = box.x + "px";
54271             this.el.dom.style.top = box.y + "px";
54272             this.updateBody(box.width, box.height);
54273         }else{
54274             this.collapsedEl.dom.style.left = box.x + "px";
54275             this.collapsedEl.dom.style.top = box.y + "px";
54276             this.collapsedEl.setSize(box.width, box.height);
54277         }
54278         if(this.tabs){
54279             this.tabs.autoSizeTabs();
54280         }
54281     },
54282
54283     updateBody : function(w, h){
54284         if(w !== null){
54285             this.el.setWidth(w);
54286             w -= this.el.getBorderWidth("rl");
54287             if(this.config.adjustments){
54288                 w += this.config.adjustments[0];
54289             }
54290         }
54291         if(h !== null){
54292             this.el.setHeight(h);
54293             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54294             h -= this.el.getBorderWidth("tb");
54295             if(this.config.adjustments){
54296                 h += this.config.adjustments[1];
54297             }
54298             this.bodyEl.setHeight(h);
54299             if(this.tabs){
54300                 h = this.tabs.syncHeight(h);
54301             }
54302         }
54303         if(this.panelSize){
54304             w = w !== null ? w : this.panelSize.width;
54305             h = h !== null ? h : this.panelSize.height;
54306         }
54307         if(this.activePanel){
54308             var el = this.activePanel.getEl();
54309             w = w !== null ? w : el.getWidth();
54310             h = h !== null ? h : el.getHeight();
54311             this.panelSize = {width: w, height: h};
54312             this.activePanel.setSize(w, h);
54313         }
54314         if(Roo.isIE && this.tabs){
54315             this.tabs.el.repaint();
54316         }
54317     },
54318
54319     /**
54320      * Returns the container element for this region.
54321      * @return {Roo.Element}
54322      */
54323     getEl : function(){
54324         return this.el;
54325     },
54326
54327     /**
54328      * Hides this region.
54329      */
54330     hide : function(){
54331         if(!this.collapsed){
54332             this.el.dom.style.left = "-2000px";
54333             this.el.hide();
54334         }else{
54335             this.collapsedEl.dom.style.left = "-2000px";
54336             this.collapsedEl.hide();
54337         }
54338         this.visible = false;
54339         this.fireEvent("visibilitychange", this, false);
54340     },
54341
54342     /**
54343      * Shows this region if it was previously hidden.
54344      */
54345     show : function(){
54346         if(!this.collapsed){
54347             this.el.show();
54348         }else{
54349             this.collapsedEl.show();
54350         }
54351         this.visible = true;
54352         this.fireEvent("visibilitychange", this, true);
54353     },
54354
54355     closeClicked : function(){
54356         if(this.activePanel){
54357             this.remove(this.activePanel);
54358         }
54359     },
54360
54361     collapseClick : function(e){
54362         if(this.isSlid){
54363            e.stopPropagation();
54364            this.slideIn();
54365         }else{
54366            e.stopPropagation();
54367            this.slideOut();
54368         }
54369     },
54370
54371     /**
54372      * Collapses this region.
54373      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54374      */
54375     collapse : function(skipAnim, skipCheck){
54376         if(this.collapsed) {
54377             return;
54378         }
54379         
54380         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54381             
54382             this.collapsed = true;
54383             if(this.split){
54384                 this.split.el.hide();
54385             }
54386             if(this.config.animate && skipAnim !== true){
54387                 this.fireEvent("invalidated", this);
54388                 this.animateCollapse();
54389             }else{
54390                 this.el.setLocation(-20000,-20000);
54391                 this.el.hide();
54392                 this.collapsedEl.show();
54393                 this.fireEvent("collapsed", this);
54394                 this.fireEvent("invalidated", this);
54395             }
54396         }
54397         
54398     },
54399
54400     animateCollapse : function(){
54401         // overridden
54402     },
54403
54404     /**
54405      * Expands this region if it was previously collapsed.
54406      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54407      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54408      */
54409     expand : function(e, skipAnim){
54410         if(e) {
54411             e.stopPropagation();
54412         }
54413         if(!this.collapsed || this.el.hasActiveFx()) {
54414             return;
54415         }
54416         if(this.isSlid){
54417             this.afterSlideIn();
54418             skipAnim = true;
54419         }
54420         this.collapsed = false;
54421         if(this.config.animate && skipAnim !== true){
54422             this.animateExpand();
54423         }else{
54424             this.el.show();
54425             if(this.split){
54426                 this.split.el.show();
54427             }
54428             this.collapsedEl.setLocation(-2000,-2000);
54429             this.collapsedEl.hide();
54430             this.fireEvent("invalidated", this);
54431             this.fireEvent("expanded", this);
54432         }
54433     },
54434
54435     animateExpand : function(){
54436         // overridden
54437     },
54438
54439     initTabs : function()
54440     {
54441         this.bodyEl.setStyle("overflow", "hidden");
54442         var ts = new Roo.TabPanel(
54443                 this.bodyEl.dom,
54444                 {
54445                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54446                     disableTooltips: this.config.disableTabTips,
54447                     toolbar : this.config.toolbar
54448                 }
54449         );
54450         if(this.config.hideTabs){
54451             ts.stripWrap.setDisplayed(false);
54452         }
54453         this.tabs = ts;
54454         ts.resizeTabs = this.config.resizeTabs === true;
54455         ts.minTabWidth = this.config.minTabWidth || 40;
54456         ts.maxTabWidth = this.config.maxTabWidth || 250;
54457         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54458         ts.monitorResize = false;
54459         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54460         ts.bodyEl.addClass('x-layout-tabs-body');
54461         this.panels.each(this.initPanelAsTab, this);
54462     },
54463
54464     initPanelAsTab : function(panel){
54465         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54466                     this.config.closeOnTab && panel.isClosable());
54467         if(panel.tabTip !== undefined){
54468             ti.setTooltip(panel.tabTip);
54469         }
54470         ti.on("activate", function(){
54471               this.setActivePanel(panel);
54472         }, this);
54473         if(this.config.closeOnTab){
54474             ti.on("beforeclose", function(t, e){
54475                 e.cancel = true;
54476                 this.remove(panel);
54477             }, this);
54478         }
54479         return ti;
54480     },
54481
54482     updatePanelTitle : function(panel, title){
54483         if(this.activePanel == panel){
54484             this.updateTitle(title);
54485         }
54486         if(this.tabs){
54487             var ti = this.tabs.getTab(panel.getEl().id);
54488             ti.setText(title);
54489             if(panel.tabTip !== undefined){
54490                 ti.setTooltip(panel.tabTip);
54491             }
54492         }
54493     },
54494
54495     updateTitle : function(title){
54496         if(this.titleTextEl && !this.config.title){
54497             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54498         }
54499     },
54500
54501     setActivePanel : function(panel){
54502         panel = this.getPanel(panel);
54503         if(this.activePanel && this.activePanel != panel){
54504             this.activePanel.setActiveState(false);
54505         }
54506         this.activePanel = panel;
54507         panel.setActiveState(true);
54508         if(this.panelSize){
54509             panel.setSize(this.panelSize.width, this.panelSize.height);
54510         }
54511         if(this.closeBtn){
54512             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54513         }
54514         this.updateTitle(panel.getTitle());
54515         if(this.tabs){
54516             this.fireEvent("invalidated", this);
54517         }
54518         this.fireEvent("panelactivated", this, panel);
54519     },
54520
54521     /**
54522      * Shows the specified panel.
54523      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54524      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54525      */
54526     showPanel : function(panel)
54527     {
54528         panel = this.getPanel(panel);
54529         if(panel){
54530             if(this.tabs){
54531                 var tab = this.tabs.getTab(panel.getEl().id);
54532                 if(tab.isHidden()){
54533                     this.tabs.unhideTab(tab.id);
54534                 }
54535                 tab.activate();
54536             }else{
54537                 this.setActivePanel(panel);
54538             }
54539         }
54540         return panel;
54541     },
54542
54543     /**
54544      * Get the active panel for this region.
54545      * @return {Roo.ContentPanel} The active panel or null
54546      */
54547     getActivePanel : function(){
54548         return this.activePanel;
54549     },
54550
54551     validateVisibility : function(){
54552         if(this.panels.getCount() < 1){
54553             this.updateTitle("&#160;");
54554             this.closeBtn.hide();
54555             this.hide();
54556         }else{
54557             if(!this.isVisible()){
54558                 this.show();
54559             }
54560         }
54561     },
54562
54563     /**
54564      * Adds the passed ContentPanel(s) to this region.
54565      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54566      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54567      */
54568     add : function(panel){
54569         if(arguments.length > 1){
54570             for(var i = 0, len = arguments.length; i < len; i++) {
54571                 this.add(arguments[i]);
54572             }
54573             return null;
54574         }
54575         if(this.hasPanel(panel)){
54576             this.showPanel(panel);
54577             return panel;
54578         }
54579         panel.setRegion(this);
54580         this.panels.add(panel);
54581         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54582             this.bodyEl.dom.appendChild(panel.getEl().dom);
54583             if(panel.background !== true){
54584                 this.setActivePanel(panel);
54585             }
54586             this.fireEvent("paneladded", this, panel);
54587             return panel;
54588         }
54589         if(!this.tabs){
54590             this.initTabs();
54591         }else{
54592             this.initPanelAsTab(panel);
54593         }
54594         if(panel.background !== true){
54595             this.tabs.activate(panel.getEl().id);
54596         }
54597         this.fireEvent("paneladded", this, panel);
54598         return panel;
54599     },
54600
54601     /**
54602      * Hides the tab for the specified panel.
54603      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54604      */
54605     hidePanel : function(panel){
54606         if(this.tabs && (panel = this.getPanel(panel))){
54607             this.tabs.hideTab(panel.getEl().id);
54608         }
54609     },
54610
54611     /**
54612      * Unhides the tab for a previously hidden panel.
54613      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54614      */
54615     unhidePanel : function(panel){
54616         if(this.tabs && (panel = this.getPanel(panel))){
54617             this.tabs.unhideTab(panel.getEl().id);
54618         }
54619     },
54620
54621     clearPanels : function(){
54622         while(this.panels.getCount() > 0){
54623              this.remove(this.panels.first());
54624         }
54625     },
54626
54627     /**
54628      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54629      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54630      * @param {Boolean} preservePanel Overrides the config preservePanel option
54631      * @return {Roo.ContentPanel} The panel that was removed
54632      */
54633     remove : function(panel, preservePanel){
54634         panel = this.getPanel(panel);
54635         if(!panel){
54636             return null;
54637         }
54638         var e = {};
54639         this.fireEvent("beforeremove", this, panel, e);
54640         if(e.cancel === true){
54641             return null;
54642         }
54643         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
54644         var panelId = panel.getId();
54645         this.panels.removeKey(panelId);
54646         if(preservePanel){
54647             document.body.appendChild(panel.getEl().dom);
54648         }
54649         if(this.tabs){
54650             this.tabs.removeTab(panel.getEl().id);
54651         }else if (!preservePanel){
54652             this.bodyEl.dom.removeChild(panel.getEl().dom);
54653         }
54654         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
54655             var p = this.panels.first();
54656             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
54657             tempEl.appendChild(p.getEl().dom);
54658             this.bodyEl.update("");
54659             this.bodyEl.dom.appendChild(p.getEl().dom);
54660             tempEl = null;
54661             this.updateTitle(p.getTitle());
54662             this.tabs = null;
54663             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54664             this.setActivePanel(p);
54665         }
54666         panel.setRegion(null);
54667         if(this.activePanel == panel){
54668             this.activePanel = null;
54669         }
54670         if(this.config.autoDestroy !== false && preservePanel !== true){
54671             try{panel.destroy();}catch(e){}
54672         }
54673         this.fireEvent("panelremoved", this, panel);
54674         return panel;
54675     },
54676
54677     /**
54678      * Returns the TabPanel component used by this region
54679      * @return {Roo.TabPanel}
54680      */
54681     getTabs : function(){
54682         return this.tabs;
54683     },
54684
54685     createTool : function(parentEl, className){
54686         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
54687             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
54688         btn.addClassOnOver("x-layout-tools-button-over");
54689         return btn;
54690     }
54691 });/*
54692  * Based on:
54693  * Ext JS Library 1.1.1
54694  * Copyright(c) 2006-2007, Ext JS, LLC.
54695  *
54696  * Originally Released Under LGPL - original licence link has changed is not relivant.
54697  *
54698  * Fork - LGPL
54699  * <script type="text/javascript">
54700  */
54701  
54702
54703
54704 /**
54705  * @class Roo.SplitLayoutRegion
54706  * @extends Roo.LayoutRegion
54707  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
54708  */
54709 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
54710     this.cursor = cursor;
54711     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
54712 };
54713
54714 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
54715     splitTip : "Drag to resize.",
54716     collapsibleSplitTip : "Drag to resize. Double click to hide.",
54717     useSplitTips : false,
54718
54719     applyConfig : function(config){
54720         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
54721         if(config.split){
54722             if(!this.split){
54723                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
54724                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
54725                 /** The SplitBar for this region 
54726                 * @type Roo.SplitBar */
54727                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
54728                 this.split.on("moved", this.onSplitMove, this);
54729                 this.split.useShim = config.useShim === true;
54730                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
54731                 if(this.useSplitTips){
54732                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
54733                 }
54734                 if(config.collapsible){
54735                     this.split.el.on("dblclick", this.collapse,  this);
54736                 }
54737             }
54738             if(typeof config.minSize != "undefined"){
54739                 this.split.minSize = config.minSize;
54740             }
54741             if(typeof config.maxSize != "undefined"){
54742                 this.split.maxSize = config.maxSize;
54743             }
54744             if(config.hideWhenEmpty || config.hidden || config.collapsed){
54745                 this.hideSplitter();
54746             }
54747         }
54748     },
54749
54750     getHMaxSize : function(){
54751          var cmax = this.config.maxSize || 10000;
54752          var center = this.mgr.getRegion("center");
54753          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
54754     },
54755
54756     getVMaxSize : function(){
54757          var cmax = this.config.maxSize || 10000;
54758          var center = this.mgr.getRegion("center");
54759          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
54760     },
54761
54762     onSplitMove : function(split, newSize){
54763         this.fireEvent("resized", this, newSize);
54764     },
54765     
54766     /** 
54767      * Returns the {@link Roo.SplitBar} for this region.
54768      * @return {Roo.SplitBar}
54769      */
54770     getSplitBar : function(){
54771         return this.split;
54772     },
54773     
54774     hide : function(){
54775         this.hideSplitter();
54776         Roo.SplitLayoutRegion.superclass.hide.call(this);
54777     },
54778
54779     hideSplitter : function(){
54780         if(this.split){
54781             this.split.el.setLocation(-2000,-2000);
54782             this.split.el.hide();
54783         }
54784     },
54785
54786     show : function(){
54787         if(this.split){
54788             this.split.el.show();
54789         }
54790         Roo.SplitLayoutRegion.superclass.show.call(this);
54791     },
54792     
54793     beforeSlide: function(){
54794         if(Roo.isGecko){// firefox overflow auto bug workaround
54795             this.bodyEl.clip();
54796             if(this.tabs) {
54797                 this.tabs.bodyEl.clip();
54798             }
54799             if(this.activePanel){
54800                 this.activePanel.getEl().clip();
54801                 
54802                 if(this.activePanel.beforeSlide){
54803                     this.activePanel.beforeSlide();
54804                 }
54805             }
54806         }
54807     },
54808     
54809     afterSlide : function(){
54810         if(Roo.isGecko){// firefox overflow auto bug workaround
54811             this.bodyEl.unclip();
54812             if(this.tabs) {
54813                 this.tabs.bodyEl.unclip();
54814             }
54815             if(this.activePanel){
54816                 this.activePanel.getEl().unclip();
54817                 if(this.activePanel.afterSlide){
54818                     this.activePanel.afterSlide();
54819                 }
54820             }
54821         }
54822     },
54823
54824     initAutoHide : function(){
54825         if(this.autoHide !== false){
54826             if(!this.autoHideHd){
54827                 var st = new Roo.util.DelayedTask(this.slideIn, this);
54828                 this.autoHideHd = {
54829                     "mouseout": function(e){
54830                         if(!e.within(this.el, true)){
54831                             st.delay(500);
54832                         }
54833                     },
54834                     "mouseover" : function(e){
54835                         st.cancel();
54836                     },
54837                     scope : this
54838                 };
54839             }
54840             this.el.on(this.autoHideHd);
54841         }
54842     },
54843
54844     clearAutoHide : function(){
54845         if(this.autoHide !== false){
54846             this.el.un("mouseout", this.autoHideHd.mouseout);
54847             this.el.un("mouseover", this.autoHideHd.mouseover);
54848         }
54849     },
54850
54851     clearMonitor : function(){
54852         Roo.get(document).un("click", this.slideInIf, this);
54853     },
54854
54855     // these names are backwards but not changed for compat
54856     slideOut : function(){
54857         if(this.isSlid || this.el.hasActiveFx()){
54858             return;
54859         }
54860         this.isSlid = true;
54861         if(this.collapseBtn){
54862             this.collapseBtn.hide();
54863         }
54864         this.closeBtnState = this.closeBtn.getStyle('display');
54865         this.closeBtn.hide();
54866         if(this.stickBtn){
54867             this.stickBtn.show();
54868         }
54869         this.el.show();
54870         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
54871         this.beforeSlide();
54872         this.el.setStyle("z-index", 10001);
54873         this.el.slideIn(this.getSlideAnchor(), {
54874             callback: function(){
54875                 this.afterSlide();
54876                 this.initAutoHide();
54877                 Roo.get(document).on("click", this.slideInIf, this);
54878                 this.fireEvent("slideshow", this);
54879             },
54880             scope: this,
54881             block: true
54882         });
54883     },
54884
54885     afterSlideIn : function(){
54886         this.clearAutoHide();
54887         this.isSlid = false;
54888         this.clearMonitor();
54889         this.el.setStyle("z-index", "");
54890         if(this.collapseBtn){
54891             this.collapseBtn.show();
54892         }
54893         this.closeBtn.setStyle('display', this.closeBtnState);
54894         if(this.stickBtn){
54895             this.stickBtn.hide();
54896         }
54897         this.fireEvent("slidehide", this);
54898     },
54899
54900     slideIn : function(cb){
54901         if(!this.isSlid || this.el.hasActiveFx()){
54902             Roo.callback(cb);
54903             return;
54904         }
54905         this.isSlid = false;
54906         this.beforeSlide();
54907         this.el.slideOut(this.getSlideAnchor(), {
54908             callback: function(){
54909                 this.el.setLeftTop(-10000, -10000);
54910                 this.afterSlide();
54911                 this.afterSlideIn();
54912                 Roo.callback(cb);
54913             },
54914             scope: this,
54915             block: true
54916         });
54917     },
54918     
54919     slideInIf : function(e){
54920         if(!e.within(this.el)){
54921             this.slideIn();
54922         }
54923     },
54924
54925     animateCollapse : function(){
54926         this.beforeSlide();
54927         this.el.setStyle("z-index", 20000);
54928         var anchor = this.getSlideAnchor();
54929         this.el.slideOut(anchor, {
54930             callback : function(){
54931                 this.el.setStyle("z-index", "");
54932                 this.collapsedEl.slideIn(anchor, {duration:.3});
54933                 this.afterSlide();
54934                 this.el.setLocation(-10000,-10000);
54935                 this.el.hide();
54936                 this.fireEvent("collapsed", this);
54937             },
54938             scope: this,
54939             block: true
54940         });
54941     },
54942
54943     animateExpand : function(){
54944         this.beforeSlide();
54945         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
54946         this.el.setStyle("z-index", 20000);
54947         this.collapsedEl.hide({
54948             duration:.1
54949         });
54950         this.el.slideIn(this.getSlideAnchor(), {
54951             callback : function(){
54952                 this.el.setStyle("z-index", "");
54953                 this.afterSlide();
54954                 if(this.split){
54955                     this.split.el.show();
54956                 }
54957                 this.fireEvent("invalidated", this);
54958                 this.fireEvent("expanded", this);
54959             },
54960             scope: this,
54961             block: true
54962         });
54963     },
54964
54965     anchors : {
54966         "west" : "left",
54967         "east" : "right",
54968         "north" : "top",
54969         "south" : "bottom"
54970     },
54971
54972     sanchors : {
54973         "west" : "l",
54974         "east" : "r",
54975         "north" : "t",
54976         "south" : "b"
54977     },
54978
54979     canchors : {
54980         "west" : "tl-tr",
54981         "east" : "tr-tl",
54982         "north" : "tl-bl",
54983         "south" : "bl-tl"
54984     },
54985
54986     getAnchor : function(){
54987         return this.anchors[this.position];
54988     },
54989
54990     getCollapseAnchor : function(){
54991         return this.canchors[this.position];
54992     },
54993
54994     getSlideAnchor : function(){
54995         return this.sanchors[this.position];
54996     },
54997
54998     getAlignAdj : function(){
54999         var cm = this.cmargins;
55000         switch(this.position){
55001             case "west":
55002                 return [0, 0];
55003             break;
55004             case "east":
55005                 return [0, 0];
55006             break;
55007             case "north":
55008                 return [0, 0];
55009             break;
55010             case "south":
55011                 return [0, 0];
55012             break;
55013         }
55014     },
55015
55016     getExpandAdj : function(){
55017         var c = this.collapsedEl, cm = this.cmargins;
55018         switch(this.position){
55019             case "west":
55020                 return [-(cm.right+c.getWidth()+cm.left), 0];
55021             break;
55022             case "east":
55023                 return [cm.right+c.getWidth()+cm.left, 0];
55024             break;
55025             case "north":
55026                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55027             break;
55028             case "south":
55029                 return [0, cm.top+cm.bottom+c.getHeight()];
55030             break;
55031         }
55032     }
55033 });/*
55034  * Based on:
55035  * Ext JS Library 1.1.1
55036  * Copyright(c) 2006-2007, Ext JS, LLC.
55037  *
55038  * Originally Released Under LGPL - original licence link has changed is not relivant.
55039  *
55040  * Fork - LGPL
55041  * <script type="text/javascript">
55042  */
55043 /*
55044  * These classes are private internal classes
55045  */
55046 Roo.CenterLayoutRegion = function(mgr, config){
55047     Roo.LayoutRegion.call(this, mgr, config, "center");
55048     this.visible = true;
55049     this.minWidth = config.minWidth || 20;
55050     this.minHeight = config.minHeight || 20;
55051 };
55052
55053 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55054     hide : function(){
55055         // center panel can't be hidden
55056     },
55057     
55058     show : function(){
55059         // center panel can't be hidden
55060     },
55061     
55062     getMinWidth: function(){
55063         return this.minWidth;
55064     },
55065     
55066     getMinHeight: function(){
55067         return this.minHeight;
55068     }
55069 });
55070
55071
55072 Roo.NorthLayoutRegion = function(mgr, config){
55073     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55074     if(this.split){
55075         this.split.placement = Roo.SplitBar.TOP;
55076         this.split.orientation = Roo.SplitBar.VERTICAL;
55077         this.split.el.addClass("x-layout-split-v");
55078     }
55079     var size = config.initialSize || config.height;
55080     if(typeof size != "undefined"){
55081         this.el.setHeight(size);
55082     }
55083 };
55084 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55085     orientation: Roo.SplitBar.VERTICAL,
55086     getBox : function(){
55087         if(this.collapsed){
55088             return this.collapsedEl.getBox();
55089         }
55090         var box = this.el.getBox();
55091         if(this.split){
55092             box.height += this.split.el.getHeight();
55093         }
55094         return box;
55095     },
55096     
55097     updateBox : function(box){
55098         if(this.split && !this.collapsed){
55099             box.height -= this.split.el.getHeight();
55100             this.split.el.setLeft(box.x);
55101             this.split.el.setTop(box.y+box.height);
55102             this.split.el.setWidth(box.width);
55103         }
55104         if(this.collapsed){
55105             this.updateBody(box.width, null);
55106         }
55107         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55108     }
55109 });
55110
55111 Roo.SouthLayoutRegion = function(mgr, config){
55112     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55113     if(this.split){
55114         this.split.placement = Roo.SplitBar.BOTTOM;
55115         this.split.orientation = Roo.SplitBar.VERTICAL;
55116         this.split.el.addClass("x-layout-split-v");
55117     }
55118     var size = config.initialSize || config.height;
55119     if(typeof size != "undefined"){
55120         this.el.setHeight(size);
55121     }
55122 };
55123 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55124     orientation: Roo.SplitBar.VERTICAL,
55125     getBox : function(){
55126         if(this.collapsed){
55127             return this.collapsedEl.getBox();
55128         }
55129         var box = this.el.getBox();
55130         if(this.split){
55131             var sh = this.split.el.getHeight();
55132             box.height += sh;
55133             box.y -= sh;
55134         }
55135         return box;
55136     },
55137     
55138     updateBox : function(box){
55139         if(this.split && !this.collapsed){
55140             var sh = this.split.el.getHeight();
55141             box.height -= sh;
55142             box.y += sh;
55143             this.split.el.setLeft(box.x);
55144             this.split.el.setTop(box.y-sh);
55145             this.split.el.setWidth(box.width);
55146         }
55147         if(this.collapsed){
55148             this.updateBody(box.width, null);
55149         }
55150         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55151     }
55152 });
55153
55154 Roo.EastLayoutRegion = function(mgr, config){
55155     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55156     if(this.split){
55157         this.split.placement = Roo.SplitBar.RIGHT;
55158         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55159         this.split.el.addClass("x-layout-split-h");
55160     }
55161     var size = config.initialSize || config.width;
55162     if(typeof size != "undefined"){
55163         this.el.setWidth(size);
55164     }
55165 };
55166 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
55167     orientation: Roo.SplitBar.HORIZONTAL,
55168     getBox : function(){
55169         if(this.collapsed){
55170             return this.collapsedEl.getBox();
55171         }
55172         var box = this.el.getBox();
55173         if(this.split){
55174             var sw = this.split.el.getWidth();
55175             box.width += sw;
55176             box.x -= sw;
55177         }
55178         return box;
55179     },
55180
55181     updateBox : function(box){
55182         if(this.split && !this.collapsed){
55183             var sw = this.split.el.getWidth();
55184             box.width -= sw;
55185             this.split.el.setLeft(box.x);
55186             this.split.el.setTop(box.y);
55187             this.split.el.setHeight(box.height);
55188             box.x += sw;
55189         }
55190         if(this.collapsed){
55191             this.updateBody(null, box.height);
55192         }
55193         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55194     }
55195 });
55196
55197 Roo.WestLayoutRegion = function(mgr, config){
55198     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55199     if(this.split){
55200         this.split.placement = Roo.SplitBar.LEFT;
55201         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55202         this.split.el.addClass("x-layout-split-h");
55203     }
55204     var size = config.initialSize || config.width;
55205     if(typeof size != "undefined"){
55206         this.el.setWidth(size);
55207     }
55208 };
55209 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55210     orientation: Roo.SplitBar.HORIZONTAL,
55211     getBox : function(){
55212         if(this.collapsed){
55213             return this.collapsedEl.getBox();
55214         }
55215         var box = this.el.getBox();
55216         if(this.split){
55217             box.width += this.split.el.getWidth();
55218         }
55219         return box;
55220     },
55221     
55222     updateBox : function(box){
55223         if(this.split && !this.collapsed){
55224             var sw = this.split.el.getWidth();
55225             box.width -= sw;
55226             this.split.el.setLeft(box.x+box.width);
55227             this.split.el.setTop(box.y);
55228             this.split.el.setHeight(box.height);
55229         }
55230         if(this.collapsed){
55231             this.updateBody(null, box.height);
55232         }
55233         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55234     }
55235 });
55236 /*
55237  * Based on:
55238  * Ext JS Library 1.1.1
55239  * Copyright(c) 2006-2007, Ext JS, LLC.
55240  *
55241  * Originally Released Under LGPL - original licence link has changed is not relivant.
55242  *
55243  * Fork - LGPL
55244  * <script type="text/javascript">
55245  */
55246  
55247  
55248 /*
55249  * Private internal class for reading and applying state
55250  */
55251 Roo.LayoutStateManager = function(layout){
55252      // default empty state
55253      this.state = {
55254         north: {},
55255         south: {},
55256         east: {},
55257         west: {}       
55258     };
55259 };
55260
55261 Roo.LayoutStateManager.prototype = {
55262     init : function(layout, provider){
55263         this.provider = provider;
55264         var state = provider.get(layout.id+"-layout-state");
55265         if(state){
55266             var wasUpdating = layout.isUpdating();
55267             if(!wasUpdating){
55268                 layout.beginUpdate();
55269             }
55270             for(var key in state){
55271                 if(typeof state[key] != "function"){
55272                     var rstate = state[key];
55273                     var r = layout.getRegion(key);
55274                     if(r && rstate){
55275                         if(rstate.size){
55276                             r.resizeTo(rstate.size);
55277                         }
55278                         if(rstate.collapsed == true){
55279                             r.collapse(true);
55280                         }else{
55281                             r.expand(null, true);
55282                         }
55283                     }
55284                 }
55285             }
55286             if(!wasUpdating){
55287                 layout.endUpdate();
55288             }
55289             this.state = state; 
55290         }
55291         this.layout = layout;
55292         layout.on("regionresized", this.onRegionResized, this);
55293         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55294         layout.on("regionexpanded", this.onRegionExpanded, this);
55295     },
55296     
55297     storeState : function(){
55298         this.provider.set(this.layout.id+"-layout-state", this.state);
55299     },
55300     
55301     onRegionResized : function(region, newSize){
55302         this.state[region.getPosition()].size = newSize;
55303         this.storeState();
55304     },
55305     
55306     onRegionCollapsed : function(region){
55307         this.state[region.getPosition()].collapsed = true;
55308         this.storeState();
55309     },
55310     
55311     onRegionExpanded : function(region){
55312         this.state[region.getPosition()].collapsed = false;
55313         this.storeState();
55314     }
55315 };/*
55316  * Based on:
55317  * Ext JS Library 1.1.1
55318  * Copyright(c) 2006-2007, Ext JS, LLC.
55319  *
55320  * Originally Released Under LGPL - original licence link has changed is not relivant.
55321  *
55322  * Fork - LGPL
55323  * <script type="text/javascript">
55324  */
55325 /**
55326  * @class Roo.ContentPanel
55327  * @extends Roo.util.Observable
55328  * @children Roo.form.Form Roo.JsonView Roo.View
55329  * @parent Roo.BorderLayout Roo.LayoutDialog builder
55330  * A basic ContentPanel element.
55331  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55332  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55333  * @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
55334  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55335  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55336  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55337  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55338  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55339  * @cfg {String} title          The title for this panel
55340  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55341  * @cfg {String} url            Calls {@link #setUrl} with this value
55342  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
55343  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55344  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55345  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55346  * @cfg {String}    style  Extra style to add to the content panel
55347  * @cfg {Roo.menu.Menu} menu  popup menu
55348
55349  * @constructor
55350  * Create a new ContentPanel.
55351  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55352  * @param {String/Object} config A string to set only the title or a config object
55353  * @param {String} content (optional) Set the HTML content for this panel
55354  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55355  */
55356 Roo.ContentPanel = function(el, config, content){
55357     
55358      
55359     /*
55360     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55361         config = el;
55362         el = Roo.id();
55363     }
55364     if (config && config.parentLayout) { 
55365         el = config.parentLayout.el.createChild(); 
55366     }
55367     */
55368     if(el.autoCreate){ // xtype is available if this is called from factory
55369         config = el;
55370         el = Roo.id();
55371     }
55372     this.el = Roo.get(el);
55373     if(!this.el && config && config.autoCreate){
55374         if(typeof config.autoCreate == "object"){
55375             if(!config.autoCreate.id){
55376                 config.autoCreate.id = config.id||el;
55377             }
55378             this.el = Roo.DomHelper.append(document.body,
55379                         config.autoCreate, true);
55380         }else{
55381             this.el = Roo.DomHelper.append(document.body,
55382                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55383         }
55384     }
55385     
55386     
55387     this.closable = false;
55388     this.loaded = false;
55389     this.active = false;
55390     if(typeof config == "string"){
55391         this.title = config;
55392     }else{
55393         Roo.apply(this, config);
55394     }
55395     
55396     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55397         this.wrapEl = this.el.wrap();
55398         this.toolbar.container = this.el.insertSibling(false, 'before');
55399         this.toolbar = new Roo.Toolbar(this.toolbar);
55400     }
55401     
55402     // xtype created footer. - not sure if will work as we normally have to render first..
55403     if (this.footer && !this.footer.el && this.footer.xtype) {
55404         if (!this.wrapEl) {
55405             this.wrapEl = this.el.wrap();
55406         }
55407     
55408         this.footer.container = this.wrapEl.createChild();
55409          
55410         this.footer = Roo.factory(this.footer, Roo);
55411         
55412     }
55413     
55414     if(this.resizeEl){
55415         this.resizeEl = Roo.get(this.resizeEl, true);
55416     }else{
55417         this.resizeEl = this.el;
55418     }
55419     // handle view.xtype
55420     
55421  
55422     
55423     
55424     this.addEvents({
55425         /**
55426          * @event activate
55427          * Fires when this panel is activated. 
55428          * @param {Roo.ContentPanel} this
55429          */
55430         "activate" : true,
55431         /**
55432          * @event deactivate
55433          * Fires when this panel is activated. 
55434          * @param {Roo.ContentPanel} this
55435          */
55436         "deactivate" : true,
55437
55438         /**
55439          * @event resize
55440          * Fires when this panel is resized if fitToFrame is true.
55441          * @param {Roo.ContentPanel} this
55442          * @param {Number} width The width after any component adjustments
55443          * @param {Number} height The height after any component adjustments
55444          */
55445         "resize" : true,
55446         
55447          /**
55448          * @event render
55449          * Fires when this tab is created
55450          * @param {Roo.ContentPanel} this
55451          */
55452         "render" : true
55453          
55454         
55455     });
55456     
55457
55458     
55459     
55460     if(this.autoScroll){
55461         this.resizeEl.setStyle("overflow", "auto");
55462     } else {
55463         // fix randome scrolling
55464         this.el.on('scroll', function() {
55465             Roo.log('fix random scolling');
55466             this.scrollTo('top',0); 
55467         });
55468     }
55469     content = content || this.content;
55470     if(content){
55471         this.setContent(content);
55472     }
55473     if(config && config.url){
55474         this.setUrl(this.url, this.params, this.loadOnce);
55475     }
55476     
55477     
55478     
55479     Roo.ContentPanel.superclass.constructor.call(this);
55480     
55481     if (this.view && typeof(this.view.xtype) != 'undefined') {
55482         this.view.el = this.el.appendChild(document.createElement("div"));
55483         this.view = Roo.factory(this.view); 
55484         this.view.render  &&  this.view.render(false, '');  
55485     }
55486     
55487     
55488     this.fireEvent('render', this);
55489 };
55490
55491 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55492     tabTip:'',
55493     setRegion : function(region){
55494         this.region = region;
55495         if(region){
55496            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55497         }else{
55498            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55499         } 
55500     },
55501     
55502     /**
55503      * Returns the toolbar for this Panel if one was configured. 
55504      * @return {Roo.Toolbar} 
55505      */
55506     getToolbar : function(){
55507         return this.toolbar;
55508     },
55509     
55510     setActiveState : function(active){
55511         this.active = active;
55512         if(!active){
55513             this.fireEvent("deactivate", this);
55514         }else{
55515             this.fireEvent("activate", this);
55516         }
55517     },
55518     /**
55519      * Updates this panel's element
55520      * @param {String} content The new content
55521      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55522     */
55523     setContent : function(content, loadScripts){
55524         this.el.update(content, loadScripts);
55525     },
55526
55527     ignoreResize : function(w, h){
55528         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55529             return true;
55530         }else{
55531             this.lastSize = {width: w, height: h};
55532             return false;
55533         }
55534     },
55535     /**
55536      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55537      * @return {Roo.UpdateManager} The UpdateManager
55538      */
55539     getUpdateManager : function(){
55540         return this.el.getUpdateManager();
55541     },
55542      /**
55543      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55544      * @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:
55545 <pre><code>
55546 panel.load({
55547     url: "your-url.php",
55548     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55549     callback: yourFunction,
55550     scope: yourObject, //(optional scope)
55551     discardUrl: false,
55552     nocache: false,
55553     text: "Loading...",
55554     timeout: 30,
55555     scripts: false
55556 });
55557 </code></pre>
55558      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55559      * 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.
55560      * @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}
55561      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55562      * @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.
55563      * @return {Roo.ContentPanel} this
55564      */
55565     load : function(){
55566         var um = this.el.getUpdateManager();
55567         um.update.apply(um, arguments);
55568         return this;
55569     },
55570
55571
55572     /**
55573      * 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.
55574      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55575      * @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)
55576      * @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)
55577      * @return {Roo.UpdateManager} The UpdateManager
55578      */
55579     setUrl : function(url, params, loadOnce){
55580         if(this.refreshDelegate){
55581             this.removeListener("activate", this.refreshDelegate);
55582         }
55583         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55584         this.on("activate", this.refreshDelegate);
55585         return this.el.getUpdateManager();
55586     },
55587     
55588     _handleRefresh : function(url, params, loadOnce){
55589         if(!loadOnce || !this.loaded){
55590             var updater = this.el.getUpdateManager();
55591             updater.update(url, params, this._setLoaded.createDelegate(this));
55592         }
55593     },
55594     
55595     _setLoaded : function(){
55596         this.loaded = true;
55597     }, 
55598     
55599     /**
55600      * Returns this panel's id
55601      * @return {String} 
55602      */
55603     getId : function(){
55604         return this.el.id;
55605     },
55606     
55607     /** 
55608      * Returns this panel's element - used by regiosn to add.
55609      * @return {Roo.Element} 
55610      */
55611     getEl : function(){
55612         return this.wrapEl || this.el;
55613     },
55614     
55615     adjustForComponents : function(width, height)
55616     {
55617         //Roo.log('adjustForComponents ');
55618         if(this.resizeEl != this.el){
55619             width -= this.el.getFrameWidth('lr');
55620             height -= this.el.getFrameWidth('tb');
55621         }
55622         if(this.toolbar){
55623             var te = this.toolbar.getEl();
55624             height -= te.getHeight();
55625             te.setWidth(width);
55626         }
55627         if(this.footer){
55628             var te = this.footer.getEl();
55629             //Roo.log("footer:" + te.getHeight());
55630             
55631             height -= te.getHeight();
55632             te.setWidth(width);
55633         }
55634         
55635         
55636         if(this.adjustments){
55637             width += this.adjustments[0];
55638             height += this.adjustments[1];
55639         }
55640         return {"width": width, "height": height};
55641     },
55642     
55643     setSize : function(width, height){
55644         if(this.fitToFrame && !this.ignoreResize(width, height)){
55645             if(this.fitContainer && this.resizeEl != this.el){
55646                 this.el.setSize(width, height);
55647             }
55648             var size = this.adjustForComponents(width, height);
55649             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
55650             this.fireEvent('resize', this, size.width, size.height);
55651         }
55652     },
55653     
55654     /**
55655      * Returns this panel's title
55656      * @return {String} 
55657      */
55658     getTitle : function(){
55659         return this.title;
55660     },
55661     
55662     /**
55663      * Set this panel's title
55664      * @param {String} title
55665      */
55666     setTitle : function(title){
55667         this.title = title;
55668         if(this.region){
55669             this.region.updatePanelTitle(this, title);
55670         }
55671     },
55672     
55673     /**
55674      * Returns true is this panel was configured to be closable
55675      * @return {Boolean} 
55676      */
55677     isClosable : function(){
55678         return this.closable;
55679     },
55680     
55681     beforeSlide : function(){
55682         this.el.clip();
55683         this.resizeEl.clip();
55684     },
55685     
55686     afterSlide : function(){
55687         this.el.unclip();
55688         this.resizeEl.unclip();
55689     },
55690     
55691     /**
55692      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
55693      *   Will fail silently if the {@link #setUrl} method has not been called.
55694      *   This does not activate the panel, just updates its content.
55695      */
55696     refresh : function(){
55697         if(this.refreshDelegate){
55698            this.loaded = false;
55699            this.refreshDelegate();
55700         }
55701     },
55702     
55703     /**
55704      * Destroys this panel
55705      */
55706     destroy : function(){
55707         this.el.removeAllListeners();
55708         var tempEl = document.createElement("span");
55709         tempEl.appendChild(this.el.dom);
55710         tempEl.innerHTML = "";
55711         this.el.remove();
55712         this.el = null;
55713     },
55714     
55715     /**
55716      * form - if the content panel contains a form - this is a reference to it.
55717      * @type {Roo.form.Form}
55718      */
55719     form : false,
55720     /**
55721      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
55722      *    This contains a reference to it.
55723      * @type {Roo.View}
55724      */
55725     view : false,
55726     
55727       /**
55728      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
55729      * <pre><code>
55730
55731 layout.addxtype({
55732        xtype : 'Form',
55733        items: [ .... ]
55734    }
55735 );
55736
55737 </code></pre>
55738      * @param {Object} cfg Xtype definition of item to add.
55739      */
55740     
55741     addxtype : function(cfg) {
55742         // add form..
55743         if (cfg.xtype.match(/^Form$/)) {
55744             
55745             var el;
55746             //if (this.footer) {
55747             //    el = this.footer.container.insertSibling(false, 'before');
55748             //} else {
55749                 el = this.el.createChild();
55750             //}
55751
55752             this.form = new  Roo.form.Form(cfg);
55753             
55754             
55755             if ( this.form.allItems.length) {
55756                 this.form.render(el.dom);
55757             }
55758             return this.form;
55759         }
55760         // should only have one of theses..
55761         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
55762             // views.. should not be just added - used named prop 'view''
55763             
55764             cfg.el = this.el.appendChild(document.createElement("div"));
55765             // factory?
55766             
55767             var ret = new Roo.factory(cfg);
55768              
55769              ret.render && ret.render(false, ''); // render blank..
55770             this.view = ret;
55771             return ret;
55772         }
55773         return false;
55774     }
55775 });
55776
55777
55778
55779
55780
55781
55782
55783
55784
55785
55786
55787
55788 /**
55789  * @class Roo.GridPanel
55790  * @extends Roo.ContentPanel
55791  * @parent Roo.BorderLayout Roo.LayoutDialog builder
55792  * @constructor
55793  * Create a new GridPanel.
55794  * @cfg {Roo.grid.Grid} grid The grid for this panel
55795  */
55796 Roo.GridPanel = function(grid, config){
55797     
55798     // universal ctor...
55799     if (typeof(grid.grid) != 'undefined') {
55800         config = grid;
55801         grid = config.grid;
55802     }
55803     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
55804         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
55805         
55806     this.wrapper.dom.appendChild(grid.getGridEl().dom);
55807     
55808     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
55809     
55810     if(this.toolbar){
55811         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
55812     }
55813     // xtype created footer. - not sure if will work as we normally have to render first..
55814     if (this.footer && !this.footer.el && this.footer.xtype) {
55815         
55816         this.footer.container = this.grid.getView().getFooterPanel(true);
55817         this.footer.dataSource = this.grid.dataSource;
55818         this.footer = Roo.factory(this.footer, Roo);
55819         
55820     }
55821     
55822     grid.monitorWindowResize = false; // turn off autosizing
55823     grid.autoHeight = false;
55824     grid.autoWidth = false;
55825     this.grid = grid;
55826     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
55827 };
55828
55829 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
55830     getId : function(){
55831         return this.grid.id;
55832     },
55833     
55834     /**
55835      * Returns the grid for this panel
55836      * @return {Roo.grid.Grid} 
55837      */
55838     getGrid : function(){
55839         return this.grid;    
55840     },
55841     
55842     setSize : function(width, height){
55843         if(!this.ignoreResize(width, height)){
55844             var grid = this.grid;
55845             var size = this.adjustForComponents(width, height);
55846             grid.getGridEl().setSize(size.width, size.height);
55847             grid.autoSize();
55848         }
55849     },
55850     
55851     beforeSlide : function(){
55852         this.grid.getView().scroller.clip();
55853     },
55854     
55855     afterSlide : function(){
55856         this.grid.getView().scroller.unclip();
55857     },
55858     
55859     destroy : function(){
55860         this.grid.destroy();
55861         delete this.grid;
55862         Roo.GridPanel.superclass.destroy.call(this); 
55863     }
55864 });
55865
55866
55867 /**
55868  * @class Roo.NestedLayoutPanel
55869  * @extends Roo.ContentPanel
55870  * @parent Roo.BorderLayout Roo.LayoutDialog builder
55871  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
55872  *
55873  * 
55874  * @constructor
55875  * Create a new NestedLayoutPanel.
55876  * 
55877  * 
55878  * @param {Roo.BorderLayout} layout [required] The layout for this panel
55879  * @param {String/Object} config A string to set only the title or a config object
55880  */
55881 Roo.NestedLayoutPanel = function(layout, config)
55882 {
55883     // construct with only one argument..
55884     /* FIXME - implement nicer consturctors
55885     if (layout.layout) {
55886         config = layout;
55887         layout = config.layout;
55888         delete config.layout;
55889     }
55890     if (layout.xtype && !layout.getEl) {
55891         // then layout needs constructing..
55892         layout = Roo.factory(layout, Roo);
55893     }
55894     */
55895     
55896     
55897     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
55898     
55899     layout.monitorWindowResize = false; // turn off autosizing
55900     this.layout = layout;
55901     this.layout.getEl().addClass("x-layout-nested-layout");
55902     
55903     
55904     
55905     
55906 };
55907
55908 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
55909
55910     layout : false,
55911
55912     setSize : function(width, height){
55913         if(!this.ignoreResize(width, height)){
55914             var size = this.adjustForComponents(width, height);
55915             var el = this.layout.getEl();
55916             el.setSize(size.width, size.height);
55917             var touch = el.dom.offsetWidth;
55918             this.layout.layout();
55919             // ie requires a double layout on the first pass
55920             if(Roo.isIE && !this.initialized){
55921                 this.initialized = true;
55922                 this.layout.layout();
55923             }
55924         }
55925     },
55926     
55927     // activate all subpanels if not currently active..
55928     
55929     setActiveState : function(active){
55930         this.active = active;
55931         if(!active){
55932             this.fireEvent("deactivate", this);
55933             return;
55934         }
55935         
55936         this.fireEvent("activate", this);
55937         // not sure if this should happen before or after..
55938         if (!this.layout) {
55939             return; // should not happen..
55940         }
55941         var reg = false;
55942         for (var r in this.layout.regions) {
55943             reg = this.layout.getRegion(r);
55944             if (reg.getActivePanel()) {
55945                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
55946                 reg.setActivePanel(reg.getActivePanel());
55947                 continue;
55948             }
55949             if (!reg.panels.length) {
55950                 continue;
55951             }
55952             reg.showPanel(reg.getPanel(0));
55953         }
55954         
55955         
55956         
55957         
55958     },
55959     
55960     /**
55961      * Returns the nested BorderLayout for this panel
55962      * @return {Roo.BorderLayout}
55963      */
55964     getLayout : function(){
55965         return this.layout;
55966     },
55967     
55968      /**
55969      * Adds a xtype elements to the layout of the nested panel
55970      * <pre><code>
55971
55972 panel.addxtype({
55973        xtype : 'ContentPanel',
55974        region: 'west',
55975        items: [ .... ]
55976    }
55977 );
55978
55979 panel.addxtype({
55980         xtype : 'NestedLayoutPanel',
55981         region: 'west',
55982         layout: {
55983            center: { },
55984            west: { }   
55985         },
55986         items : [ ... list of content panels or nested layout panels.. ]
55987    }
55988 );
55989 </code></pre>
55990      * @param {Object} cfg Xtype definition of item to add.
55991      */
55992     addxtype : function(cfg) {
55993         return this.layout.addxtype(cfg);
55994     
55995     }
55996 });
55997
55998 Roo.ScrollPanel = function(el, config, content){
55999     config = config || {};
56000     config.fitToFrame = true;
56001     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56002     
56003     this.el.dom.style.overflow = "hidden";
56004     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56005     this.el.removeClass("x-layout-inactive-content");
56006     this.el.on("mousewheel", this.onWheel, this);
56007
56008     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56009     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56010     up.unselectable(); down.unselectable();
56011     up.on("click", this.scrollUp, this);
56012     down.on("click", this.scrollDown, this);
56013     up.addClassOnOver("x-scroller-btn-over");
56014     down.addClassOnOver("x-scroller-btn-over");
56015     up.addClassOnClick("x-scroller-btn-click");
56016     down.addClassOnClick("x-scroller-btn-click");
56017     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56018
56019     this.resizeEl = this.el;
56020     this.el = wrap; this.up = up; this.down = down;
56021 };
56022
56023 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56024     increment : 100,
56025     wheelIncrement : 5,
56026     scrollUp : function(){
56027         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56028     },
56029
56030     scrollDown : function(){
56031         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56032     },
56033
56034     afterScroll : function(){
56035         var el = this.resizeEl;
56036         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56037         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56038         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56039     },
56040
56041     setSize : function(){
56042         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56043         this.afterScroll();
56044     },
56045
56046     onWheel : function(e){
56047         var d = e.getWheelDelta();
56048         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56049         this.afterScroll();
56050         e.stopEvent();
56051     },
56052
56053     setContent : function(content, loadScripts){
56054         this.resizeEl.update(content, loadScripts);
56055     }
56056
56057 });
56058
56059
56060
56061 /**
56062  * @class Roo.TreePanel
56063  * @extends Roo.ContentPanel
56064  * @parent Roo.BorderLayout Roo.LayoutDialog builder
56065  * Treepanel component
56066  * 
56067  * @constructor
56068  * Create a new TreePanel. - defaults to fit/scoll contents.
56069  * @param {String/Object} config A string to set only the panel's title, or a config object
56070  */
56071 Roo.TreePanel = function(config){
56072     var el = config.el;
56073     var tree = config.tree;
56074     delete config.tree; 
56075     delete config.el; // hopefull!
56076     
56077     // wrapper for IE7 strict & safari scroll issue
56078     
56079     var treeEl = el.createChild();
56080     config.resizeEl = treeEl;
56081     
56082     
56083     
56084     Roo.TreePanel.superclass.constructor.call(this, el, config);
56085  
56086  
56087     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56088     //console.log(tree);
56089     this.on('activate', function()
56090     {
56091         if (this.tree.rendered) {
56092             return;
56093         }
56094         //console.log('render tree');
56095         this.tree.render();
56096     });
56097     // this should not be needed.. - it's actually the 'el' that resizes?
56098     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56099     
56100     //this.on('resize',  function (cp, w, h) {
56101     //        this.tree.innerCt.setWidth(w);
56102     //        this.tree.innerCt.setHeight(h);
56103     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56104     //});
56105
56106         
56107     
56108 };
56109
56110 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56111     fitToFrame : true,
56112     autoScroll : true,
56113     /*
56114      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56115      */
56116     tree : false
56117
56118 });
56119 /*
56120  * Based on:
56121  * Ext JS Library 1.1.1
56122  * Copyright(c) 2006-2007, Ext JS, LLC.
56123  *
56124  * Originally Released Under LGPL - original licence link has changed is not relivant.
56125  *
56126  * Fork - LGPL
56127  * <script type="text/javascript">
56128  */
56129  
56130
56131 /**
56132  * @class Roo.ReaderLayout
56133  * @extends Roo.BorderLayout
56134  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56135  * center region containing two nested regions (a top one for a list view and one for item preview below),
56136  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56137  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56138  * expedites the setup of the overall layout and regions for this common application style.
56139  * Example:
56140  <pre><code>
56141 var reader = new Roo.ReaderLayout();
56142 var CP = Roo.ContentPanel;  // shortcut for adding
56143
56144 reader.beginUpdate();
56145 reader.add("north", new CP("north", "North"));
56146 reader.add("west", new CP("west", {title: "West"}));
56147 reader.add("east", new CP("east", {title: "East"}));
56148
56149 reader.regions.listView.add(new CP("listView", "List"));
56150 reader.regions.preview.add(new CP("preview", "Preview"));
56151 reader.endUpdate();
56152 </code></pre>
56153 * @constructor
56154 * Create a new ReaderLayout
56155 * @param {Object} config Configuration options
56156 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56157 * document.body if omitted)
56158 */
56159 Roo.ReaderLayout = function(config, renderTo){
56160     var c = config || {size:{}};
56161     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56162         north: c.north !== false ? Roo.apply({
56163             split:false,
56164             initialSize: 32,
56165             titlebar: false
56166         }, c.north) : false,
56167         west: c.west !== false ? Roo.apply({
56168             split:true,
56169             initialSize: 200,
56170             minSize: 175,
56171             maxSize: 400,
56172             titlebar: true,
56173             collapsible: true,
56174             animate: true,
56175             margins:{left:5,right:0,bottom:5,top:5},
56176             cmargins:{left:5,right:5,bottom:5,top:5}
56177         }, c.west) : false,
56178         east: c.east !== false ? Roo.apply({
56179             split:true,
56180             initialSize: 200,
56181             minSize: 175,
56182             maxSize: 400,
56183             titlebar: true,
56184             collapsible: true,
56185             animate: true,
56186             margins:{left:0,right:5,bottom:5,top:5},
56187             cmargins:{left:5,right:5,bottom:5,top:5}
56188         }, c.east) : false,
56189         center: Roo.apply({
56190             tabPosition: 'top',
56191             autoScroll:false,
56192             closeOnTab: true,
56193             titlebar:false,
56194             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56195         }, c.center)
56196     });
56197
56198     this.el.addClass('x-reader');
56199
56200     this.beginUpdate();
56201
56202     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56203         south: c.preview !== false ? Roo.apply({
56204             split:true,
56205             initialSize: 200,
56206             minSize: 100,
56207             autoScroll:true,
56208             collapsible:true,
56209             titlebar: true,
56210             cmargins:{top:5,left:0, right:0, bottom:0}
56211         }, c.preview) : false,
56212         center: Roo.apply({
56213             autoScroll:false,
56214             titlebar:false,
56215             minHeight:200
56216         }, c.listView)
56217     });
56218     this.add('center', new Roo.NestedLayoutPanel(inner,
56219             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56220
56221     this.endUpdate();
56222
56223     this.regions.preview = inner.getRegion('south');
56224     this.regions.listView = inner.getRegion('center');
56225 };
56226
56227 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56228  * Based on:
56229  * Ext JS Library 1.1.1
56230  * Copyright(c) 2006-2007, Ext JS, LLC.
56231  *
56232  * Originally Released Under LGPL - original licence link has changed is not relivant.
56233  *
56234  * Fork - LGPL
56235  * <script type="text/javascript">
56236  */
56237  
56238 /**
56239  * @class Roo.grid.Grid
56240  * @extends Roo.util.Observable
56241  * This class represents the primary interface of a component based grid control.
56242  * <br><br>Usage:<pre><code>
56243  var grid = new Roo.grid.Grid("my-container-id", {
56244      ds: myDataStore,
56245      cm: myColModel,
56246      selModel: mySelectionModel,
56247      autoSizeColumns: true,
56248      monitorWindowResize: false,
56249      trackMouseOver: true
56250  });
56251  // set any options
56252  grid.render();
56253  * </code></pre>
56254  * <b>Common Problems:</b><br/>
56255  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56256  * element will correct this<br/>
56257  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56258  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56259  * are unpredictable.<br/>
56260  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56261  * grid to calculate dimensions/offsets.<br/>
56262   * @constructor
56263  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56264  * The container MUST have some type of size defined for the grid to fill. The container will be
56265  * automatically set to position relative if it isn't already.
56266  * @param {Object} config A config object that sets properties on this grid.
56267  */
56268 Roo.grid.Grid = function(container, config){
56269         // initialize the container
56270         this.container = Roo.get(container);
56271         this.container.update("");
56272         this.container.setStyle("overflow", "hidden");
56273     this.container.addClass('x-grid-container');
56274
56275     this.id = this.container.id;
56276
56277     Roo.apply(this, config);
56278     // check and correct shorthanded configs
56279     if(this.ds){
56280         this.dataSource = this.ds;
56281         delete this.ds;
56282     }
56283     if(this.cm){
56284         this.colModel = this.cm;
56285         delete this.cm;
56286     }
56287     if(this.sm){
56288         this.selModel = this.sm;
56289         delete this.sm;
56290     }
56291
56292     if (this.selModel) {
56293         this.selModel = Roo.factory(this.selModel, Roo.grid);
56294         this.sm = this.selModel;
56295         this.sm.xmodule = this.xmodule || false;
56296     }
56297     if (typeof(this.colModel.config) == 'undefined') {
56298         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56299         this.cm = this.colModel;
56300         this.cm.xmodule = this.xmodule || false;
56301     }
56302     if (this.dataSource) {
56303         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56304         this.ds = this.dataSource;
56305         this.ds.xmodule = this.xmodule || false;
56306          
56307     }
56308     
56309     
56310     
56311     if(this.width){
56312         this.container.setWidth(this.width);
56313     }
56314
56315     if(this.height){
56316         this.container.setHeight(this.height);
56317     }
56318     /** @private */
56319         this.addEvents({
56320         // raw events
56321         /**
56322          * @event click
56323          * The raw click event for the entire grid.
56324          * @param {Roo.EventObject} e
56325          */
56326         "click" : true,
56327         /**
56328          * @event dblclick
56329          * The raw dblclick event for the entire grid.
56330          * @param {Roo.EventObject} e
56331          */
56332         "dblclick" : true,
56333         /**
56334          * @event contextmenu
56335          * The raw contextmenu event for the entire grid.
56336          * @param {Roo.EventObject} e
56337          */
56338         "contextmenu" : true,
56339         /**
56340          * @event mousedown
56341          * The raw mousedown event for the entire grid.
56342          * @param {Roo.EventObject} e
56343          */
56344         "mousedown" : true,
56345         /**
56346          * @event mouseup
56347          * The raw mouseup event for the entire grid.
56348          * @param {Roo.EventObject} e
56349          */
56350         "mouseup" : true,
56351         /**
56352          * @event mouseover
56353          * The raw mouseover event for the entire grid.
56354          * @param {Roo.EventObject} e
56355          */
56356         "mouseover" : true,
56357         /**
56358          * @event mouseout
56359          * The raw mouseout event for the entire grid.
56360          * @param {Roo.EventObject} e
56361          */
56362         "mouseout" : true,
56363         /**
56364          * @event keypress
56365          * The raw keypress event for the entire grid.
56366          * @param {Roo.EventObject} e
56367          */
56368         "keypress" : true,
56369         /**
56370          * @event keydown
56371          * The raw keydown event for the entire grid.
56372          * @param {Roo.EventObject} e
56373          */
56374         "keydown" : true,
56375
56376         // custom events
56377
56378         /**
56379          * @event cellclick
56380          * Fires when a cell is clicked
56381          * @param {Grid} this
56382          * @param {Number} rowIndex
56383          * @param {Number} columnIndex
56384          * @param {Roo.EventObject} e
56385          */
56386         "cellclick" : true,
56387         /**
56388          * @event celldblclick
56389          * Fires when a cell is double clicked
56390          * @param {Grid} this
56391          * @param {Number} rowIndex
56392          * @param {Number} columnIndex
56393          * @param {Roo.EventObject} e
56394          */
56395         "celldblclick" : true,
56396         /**
56397          * @event rowclick
56398          * Fires when a row is clicked
56399          * @param {Grid} this
56400          * @param {Number} rowIndex
56401          * @param {Roo.EventObject} e
56402          */
56403         "rowclick" : true,
56404         /**
56405          * @event rowdblclick
56406          * Fires when a row is double clicked
56407          * @param {Grid} this
56408          * @param {Number} rowIndex
56409          * @param {Roo.EventObject} e
56410          */
56411         "rowdblclick" : true,
56412         /**
56413          * @event headerclick
56414          * Fires when a header is clicked
56415          * @param {Grid} this
56416          * @param {Number} columnIndex
56417          * @param {Roo.EventObject} e
56418          */
56419         "headerclick" : true,
56420         /**
56421          * @event headerdblclick
56422          * Fires when a header cell is double clicked
56423          * @param {Grid} this
56424          * @param {Number} columnIndex
56425          * @param {Roo.EventObject} e
56426          */
56427         "headerdblclick" : true,
56428         /**
56429          * @event rowcontextmenu
56430          * Fires when a row is right clicked
56431          * @param {Grid} this
56432          * @param {Number} rowIndex
56433          * @param {Roo.EventObject} e
56434          */
56435         "rowcontextmenu" : true,
56436         /**
56437          * @event cellcontextmenu
56438          * Fires when a cell is right clicked
56439          * @param {Grid} this
56440          * @param {Number} rowIndex
56441          * @param {Number} cellIndex
56442          * @param {Roo.EventObject} e
56443          */
56444          "cellcontextmenu" : true,
56445         /**
56446          * @event headercontextmenu
56447          * Fires when a header is right clicked
56448          * @param {Grid} this
56449          * @param {Number} columnIndex
56450          * @param {Roo.EventObject} e
56451          */
56452         "headercontextmenu" : true,
56453         /**
56454          * @event bodyscroll
56455          * Fires when the body element is scrolled
56456          * @param {Number} scrollLeft
56457          * @param {Number} scrollTop
56458          */
56459         "bodyscroll" : true,
56460         /**
56461          * @event columnresize
56462          * Fires when the user resizes a column
56463          * @param {Number} columnIndex
56464          * @param {Number} newSize
56465          */
56466         "columnresize" : true,
56467         /**
56468          * @event columnmove
56469          * Fires when the user moves a column
56470          * @param {Number} oldIndex
56471          * @param {Number} newIndex
56472          */
56473         "columnmove" : true,
56474         /**
56475          * @event startdrag
56476          * Fires when row(s) start being dragged
56477          * @param {Grid} this
56478          * @param {Roo.GridDD} dd The drag drop object
56479          * @param {event} e The raw browser event
56480          */
56481         "startdrag" : true,
56482         /**
56483          * @event enddrag
56484          * Fires when a drag operation is complete
56485          * @param {Grid} this
56486          * @param {Roo.GridDD} dd The drag drop object
56487          * @param {event} e The raw browser event
56488          */
56489         "enddrag" : true,
56490         /**
56491          * @event dragdrop
56492          * Fires when dragged row(s) are dropped on a valid DD target
56493          * @param {Grid} this
56494          * @param {Roo.GridDD} dd The drag drop object
56495          * @param {String} targetId The target drag drop object
56496          * @param {event} e The raw browser event
56497          */
56498         "dragdrop" : true,
56499         /**
56500          * @event dragover
56501          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56502          * @param {Grid} this
56503          * @param {Roo.GridDD} dd The drag drop object
56504          * @param {String} targetId The target drag drop object
56505          * @param {event} e The raw browser event
56506          */
56507         "dragover" : true,
56508         /**
56509          * @event dragenter
56510          *  Fires when the dragged row(s) first cross another DD target while being dragged
56511          * @param {Grid} this
56512          * @param {Roo.GridDD} dd The drag drop object
56513          * @param {String} targetId The target drag drop object
56514          * @param {event} e The raw browser event
56515          */
56516         "dragenter" : true,
56517         /**
56518          * @event dragout
56519          * Fires when the dragged row(s) leave another DD target while being dragged
56520          * @param {Grid} this
56521          * @param {Roo.GridDD} dd The drag drop object
56522          * @param {String} targetId The target drag drop object
56523          * @param {event} e The raw browser event
56524          */
56525         "dragout" : true,
56526         /**
56527          * @event rowclass
56528          * Fires when a row is rendered, so you can change add a style to it.
56529          * @param {GridView} gridview   The grid view
56530          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56531          */
56532         'rowclass' : true,
56533
56534         /**
56535          * @event render
56536          * Fires when the grid is rendered
56537          * @param {Grid} grid
56538          */
56539         'render' : true
56540     });
56541
56542     Roo.grid.Grid.superclass.constructor.call(this);
56543 };
56544 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56545     
56546     /**
56547          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56548          */
56549         /**
56550          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56551          */
56552         /**
56553          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56554          */
56555         /**
56556          * @cfg {Roo.grid.Store} ds The data store for the grid
56557          */
56558         /**
56559          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56560          */
56561         /**
56562      * @cfg {String} ddGroup - drag drop group.
56563      */
56564       /**
56565      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56566      */
56567
56568     /**
56569      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56570      */
56571     minColumnWidth : 25,
56572
56573     /**
56574      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56575      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56576      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56577      */
56578     autoSizeColumns : false,
56579
56580     /**
56581      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56582      */
56583     autoSizeHeaders : true,
56584
56585     /**
56586      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56587      */
56588     monitorWindowResize : true,
56589
56590     /**
56591      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56592      * rows measured to get a columns size. Default is 0 (all rows).
56593      */
56594     maxRowsToMeasure : 0,
56595
56596     /**
56597      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56598      */
56599     trackMouseOver : true,
56600
56601     /**
56602     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56603     */
56604       /**
56605     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56606     */
56607     
56608     /**
56609     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56610     */
56611     enableDragDrop : false,
56612     
56613     /**
56614     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56615     */
56616     enableColumnMove : true,
56617     
56618     /**
56619     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56620     */
56621     enableColumnHide : true,
56622     
56623     /**
56624     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56625     */
56626     enableRowHeightSync : false,
56627     
56628     /**
56629     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56630     */
56631     stripeRows : true,
56632     
56633     /**
56634     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
56635     */
56636     autoHeight : false,
56637
56638     /**
56639      * @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.
56640      */
56641     autoExpandColumn : false,
56642
56643     /**
56644     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
56645     * Default is 50.
56646     */
56647     autoExpandMin : 50,
56648
56649     /**
56650     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
56651     */
56652     autoExpandMax : 1000,
56653
56654     /**
56655     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
56656     */
56657     view : null,
56658
56659     /**
56660     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
56661     */
56662     loadMask : false,
56663     /**
56664     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
56665     */
56666     dropTarget: false,
56667      /**
56668     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
56669     */ 
56670     sortColMenu : false,
56671     
56672     // private
56673     rendered : false,
56674
56675     /**
56676     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
56677     * of a fixed width. Default is false.
56678     */
56679     /**
56680     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
56681     */
56682     
56683     
56684     /**
56685     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
56686     * %0 is replaced with the number of selected rows.
56687     */
56688     ddText : "{0} selected row{1}",
56689     
56690     
56691     /**
56692      * Called once after all setup has been completed and the grid is ready to be rendered.
56693      * @return {Roo.grid.Grid} this
56694      */
56695     render : function()
56696     {
56697         var c = this.container;
56698         // try to detect autoHeight/width mode
56699         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
56700             this.autoHeight = true;
56701         }
56702         var view = this.getView();
56703         view.init(this);
56704
56705         c.on("click", this.onClick, this);
56706         c.on("dblclick", this.onDblClick, this);
56707         c.on("contextmenu", this.onContextMenu, this);
56708         c.on("keydown", this.onKeyDown, this);
56709         if (Roo.isTouch) {
56710             c.on("touchstart", this.onTouchStart, this);
56711         }
56712
56713         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
56714
56715         this.getSelectionModel().init(this);
56716
56717         view.render();
56718
56719         if(this.loadMask){
56720             this.loadMask = new Roo.LoadMask(this.container,
56721                     Roo.apply({store:this.dataSource}, this.loadMask));
56722         }
56723         
56724         
56725         if (this.toolbar && this.toolbar.xtype) {
56726             this.toolbar.container = this.getView().getHeaderPanel(true);
56727             this.toolbar = new Roo.Toolbar(this.toolbar);
56728         }
56729         if (this.footer && this.footer.xtype) {
56730             this.footer.dataSource = this.getDataSource();
56731             this.footer.container = this.getView().getFooterPanel(true);
56732             this.footer = Roo.factory(this.footer, Roo);
56733         }
56734         if (this.dropTarget && this.dropTarget.xtype) {
56735             delete this.dropTarget.xtype;
56736             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
56737         }
56738         
56739         
56740         this.rendered = true;
56741         this.fireEvent('render', this);
56742         return this;
56743     },
56744
56745     /**
56746      * Reconfigures the grid to use a different Store and Column Model.
56747      * The View will be bound to the new objects and refreshed.
56748      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
56749      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
56750      */
56751     reconfigure : function(dataSource, colModel){
56752         if(this.loadMask){
56753             this.loadMask.destroy();
56754             this.loadMask = new Roo.LoadMask(this.container,
56755                     Roo.apply({store:dataSource}, this.loadMask));
56756         }
56757         this.view.bind(dataSource, colModel);
56758         this.dataSource = dataSource;
56759         this.colModel = colModel;
56760         this.view.refresh(true);
56761     },
56762     /**
56763      * addColumns
56764      * Add's a column, default at the end..
56765      
56766      * @param {int} position to add (default end)
56767      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
56768      */
56769     addColumns : function(pos, ar)
56770     {
56771         
56772         for (var i =0;i< ar.length;i++) {
56773             var cfg = ar[i];
56774             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
56775             this.cm.lookup[cfg.id] = cfg;
56776         }
56777         
56778         
56779         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
56780             pos = this.cm.config.length; //this.cm.config.push(cfg);
56781         } 
56782         pos = Math.max(0,pos);
56783         ar.unshift(0);
56784         ar.unshift(pos);
56785         this.cm.config.splice.apply(this.cm.config, ar);
56786         
56787         
56788         
56789         this.view.generateRules(this.cm);
56790         this.view.refresh(true);
56791         
56792     },
56793     
56794     
56795     
56796     
56797     // private
56798     onKeyDown : function(e){
56799         this.fireEvent("keydown", e);
56800     },
56801
56802     /**
56803      * Destroy this grid.
56804      * @param {Boolean} removeEl True to remove the element
56805      */
56806     destroy : function(removeEl, keepListeners){
56807         if(this.loadMask){
56808             this.loadMask.destroy();
56809         }
56810         var c = this.container;
56811         c.removeAllListeners();
56812         this.view.destroy();
56813         this.colModel.purgeListeners();
56814         if(!keepListeners){
56815             this.purgeListeners();
56816         }
56817         c.update("");
56818         if(removeEl === true){
56819             c.remove();
56820         }
56821     },
56822
56823     // private
56824     processEvent : function(name, e){
56825         // does this fire select???
56826         //Roo.log('grid:processEvent '  + name);
56827         
56828         if (name != 'touchstart' ) {
56829             this.fireEvent(name, e);    
56830         }
56831         
56832         var t = e.getTarget();
56833         var v = this.view;
56834         var header = v.findHeaderIndex(t);
56835         if(header !== false){
56836             var ename = name == 'touchstart' ? 'click' : name;
56837              
56838             this.fireEvent("header" + ename, this, header, e);
56839         }else{
56840             var row = v.findRowIndex(t);
56841             var cell = v.findCellIndex(t);
56842             if (name == 'touchstart') {
56843                 // first touch is always a click.
56844                 // hopefull this happens after selection is updated.?
56845                 name = false;
56846                 
56847                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
56848                     var cs = this.selModel.getSelectedCell();
56849                     if (row == cs[0] && cell == cs[1]){
56850                         name = 'dblclick';
56851                     }
56852                 }
56853                 if (typeof(this.selModel.getSelections) != 'undefined') {
56854                     var cs = this.selModel.getSelections();
56855                     var ds = this.dataSource;
56856                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
56857                         name = 'dblclick';
56858                     }
56859                 }
56860                 if (!name) {
56861                     return;
56862                 }
56863             }
56864             
56865             
56866             if(row !== false){
56867                 this.fireEvent("row" + name, this, row, e);
56868                 if(cell !== false){
56869                     this.fireEvent("cell" + name, this, row, cell, e);
56870                 }
56871             }
56872         }
56873     },
56874
56875     // private
56876     onClick : function(e){
56877         this.processEvent("click", e);
56878     },
56879    // private
56880     onTouchStart : function(e){
56881         this.processEvent("touchstart", e);
56882     },
56883
56884     // private
56885     onContextMenu : function(e, t){
56886         this.processEvent("contextmenu", e);
56887     },
56888
56889     // private
56890     onDblClick : function(e){
56891         this.processEvent("dblclick", e);
56892     },
56893
56894     // private
56895     walkCells : function(row, col, step, fn, scope){
56896         var cm = this.colModel, clen = cm.getColumnCount();
56897         var ds = this.dataSource, rlen = ds.getCount(), first = true;
56898         if(step < 0){
56899             if(col < 0){
56900                 row--;
56901                 first = false;
56902             }
56903             while(row >= 0){
56904                 if(!first){
56905                     col = clen-1;
56906                 }
56907                 first = false;
56908                 while(col >= 0){
56909                     if(fn.call(scope || this, row, col, cm) === true){
56910                         return [row, col];
56911                     }
56912                     col--;
56913                 }
56914                 row--;
56915             }
56916         } else {
56917             if(col >= clen){
56918                 row++;
56919                 first = false;
56920             }
56921             while(row < rlen){
56922                 if(!first){
56923                     col = 0;
56924                 }
56925                 first = false;
56926                 while(col < clen){
56927                     if(fn.call(scope || this, row, col, cm) === true){
56928                         return [row, col];
56929                     }
56930                     col++;
56931                 }
56932                 row++;
56933             }
56934         }
56935         return null;
56936     },
56937
56938     // private
56939     getSelections : function(){
56940         return this.selModel.getSelections();
56941     },
56942
56943     /**
56944      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
56945      * but if manual update is required this method will initiate it.
56946      */
56947     autoSize : function(){
56948         if(this.rendered){
56949             this.view.layout();
56950             if(this.view.adjustForScroll){
56951                 this.view.adjustForScroll();
56952             }
56953         }
56954     },
56955
56956     /**
56957      * Returns the grid's underlying element.
56958      * @return {Element} The element
56959      */
56960     getGridEl : function(){
56961         return this.container;
56962     },
56963
56964     // private for compatibility, overridden by editor grid
56965     stopEditing : function(){},
56966
56967     /**
56968      * Returns the grid's SelectionModel.
56969      * @return {SelectionModel}
56970      */
56971     getSelectionModel : function(){
56972         if(!this.selModel){
56973             this.selModel = new Roo.grid.RowSelectionModel();
56974         }
56975         return this.selModel;
56976     },
56977
56978     /**
56979      * Returns the grid's DataSource.
56980      * @return {DataSource}
56981      */
56982     getDataSource : function(){
56983         return this.dataSource;
56984     },
56985
56986     /**
56987      * Returns the grid's ColumnModel.
56988      * @return {ColumnModel}
56989      */
56990     getColumnModel : function(){
56991         return this.colModel;
56992     },
56993
56994     /**
56995      * Returns the grid's GridView object.
56996      * @return {GridView}
56997      */
56998     getView : function(){
56999         if(!this.view){
57000             this.view = new Roo.grid.GridView(this.viewConfig);
57001             this.relayEvents(this.view, [
57002                 "beforerowremoved", "beforerowsinserted",
57003                 "beforerefresh", "rowremoved",
57004                 "rowsinserted", "rowupdated" ,"refresh"
57005             ]);
57006         }
57007         return this.view;
57008     },
57009     /**
57010      * Called to get grid's drag proxy text, by default returns this.ddText.
57011      * Override this to put something different in the dragged text.
57012      * @return {String}
57013      */
57014     getDragDropText : function(){
57015         var count = this.selModel.getCount();
57016         return String.format(this.ddText, count, count == 1 ? '' : 's');
57017     }
57018 });
57019 /*
57020  * Based on:
57021  * Ext JS Library 1.1.1
57022  * Copyright(c) 2006-2007, Ext JS, LLC.
57023  *
57024  * Originally Released Under LGPL - original licence link has changed is not relivant.
57025  *
57026  * Fork - LGPL
57027  * <script type="text/javascript">
57028  */
57029  /**
57030  * @class Roo.grid.AbstractGridView
57031  * @extends Roo.util.Observable
57032  * @abstract
57033  * Abstract base class for grid Views
57034  * @constructor
57035  */
57036 Roo.grid.AbstractGridView = function(){
57037         this.grid = null;
57038         
57039         this.events = {
57040             "beforerowremoved" : true,
57041             "beforerowsinserted" : true,
57042             "beforerefresh" : true,
57043             "rowremoved" : true,
57044             "rowsinserted" : true,
57045             "rowupdated" : true,
57046             "refresh" : true
57047         };
57048     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57049 };
57050
57051 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57052     rowClass : "x-grid-row",
57053     cellClass : "x-grid-cell",
57054     tdClass : "x-grid-td",
57055     hdClass : "x-grid-hd",
57056     splitClass : "x-grid-hd-split",
57057     
57058     init: function(grid){
57059         this.grid = grid;
57060                 var cid = this.grid.getGridEl().id;
57061         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57062         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57063         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57064         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57065         },
57066         
57067     getColumnRenderers : function(){
57068         var renderers = [];
57069         var cm = this.grid.colModel;
57070         var colCount = cm.getColumnCount();
57071         for(var i = 0; i < colCount; i++){
57072             renderers[i] = cm.getRenderer(i);
57073         }
57074         return renderers;
57075     },
57076     
57077     getColumnIds : function(){
57078         var ids = [];
57079         var cm = this.grid.colModel;
57080         var colCount = cm.getColumnCount();
57081         for(var i = 0; i < colCount; i++){
57082             ids[i] = cm.getColumnId(i);
57083         }
57084         return ids;
57085     },
57086     
57087     getDataIndexes : function(){
57088         if(!this.indexMap){
57089             this.indexMap = this.buildIndexMap();
57090         }
57091         return this.indexMap.colToData;
57092     },
57093     
57094     getColumnIndexByDataIndex : function(dataIndex){
57095         if(!this.indexMap){
57096             this.indexMap = this.buildIndexMap();
57097         }
57098         return this.indexMap.dataToCol[dataIndex];
57099     },
57100     
57101     /**
57102      * Set a css style for a column dynamically. 
57103      * @param {Number} colIndex The index of the column
57104      * @param {String} name The css property name
57105      * @param {String} value The css value
57106      */
57107     setCSSStyle : function(colIndex, name, value){
57108         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57109         Roo.util.CSS.updateRule(selector, name, value);
57110     },
57111     
57112     generateRules : function(cm){
57113         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57114         Roo.util.CSS.removeStyleSheet(rulesId);
57115         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57116             var cid = cm.getColumnId(i);
57117             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57118                          this.tdSelector, cid, " {\n}\n",
57119                          this.hdSelector, cid, " {\n}\n",
57120                          this.splitSelector, cid, " {\n}\n");
57121         }
57122         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57123     }
57124 });/*
57125  * Based on:
57126  * Ext JS Library 1.1.1
57127  * Copyright(c) 2006-2007, Ext JS, LLC.
57128  *
57129  * Originally Released Under LGPL - original licence link has changed is not relivant.
57130  *
57131  * Fork - LGPL
57132  * <script type="text/javascript">
57133  */
57134
57135 // private
57136 // This is a support class used internally by the Grid components
57137 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57138     this.grid = grid;
57139     this.view = grid.getView();
57140     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57141     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57142     if(hd2){
57143         this.setHandleElId(Roo.id(hd));
57144         this.setOuterHandleElId(Roo.id(hd2));
57145     }
57146     this.scroll = false;
57147 };
57148 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57149     maxDragWidth: 120,
57150     getDragData : function(e){
57151         var t = Roo.lib.Event.getTarget(e);
57152         var h = this.view.findHeaderCell(t);
57153         if(h){
57154             return {ddel: h.firstChild, header:h};
57155         }
57156         return false;
57157     },
57158
57159     onInitDrag : function(e){
57160         this.view.headersDisabled = true;
57161         var clone = this.dragData.ddel.cloneNode(true);
57162         clone.id = Roo.id();
57163         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57164         this.proxy.update(clone);
57165         return true;
57166     },
57167
57168     afterValidDrop : function(){
57169         var v = this.view;
57170         setTimeout(function(){
57171             v.headersDisabled = false;
57172         }, 50);
57173     },
57174
57175     afterInvalidDrop : function(){
57176         var v = this.view;
57177         setTimeout(function(){
57178             v.headersDisabled = false;
57179         }, 50);
57180     }
57181 });
57182 /*
57183  * Based on:
57184  * Ext JS Library 1.1.1
57185  * Copyright(c) 2006-2007, Ext JS, LLC.
57186  *
57187  * Originally Released Under LGPL - original licence link has changed is not relivant.
57188  *
57189  * Fork - LGPL
57190  * <script type="text/javascript">
57191  */
57192 // private
57193 // This is a support class used internally by the Grid components
57194 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57195     this.grid = grid;
57196     this.view = grid.getView();
57197     // split the proxies so they don't interfere with mouse events
57198     this.proxyTop = Roo.DomHelper.append(document.body, {
57199         cls:"col-move-top", html:"&#160;"
57200     }, true);
57201     this.proxyBottom = Roo.DomHelper.append(document.body, {
57202         cls:"col-move-bottom", html:"&#160;"
57203     }, true);
57204     this.proxyTop.hide = this.proxyBottom.hide = function(){
57205         this.setLeftTop(-100,-100);
57206         this.setStyle("visibility", "hidden");
57207     };
57208     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57209     // temporarily disabled
57210     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57211     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57212 };
57213 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57214     proxyOffsets : [-4, -9],
57215     fly: Roo.Element.fly,
57216
57217     getTargetFromEvent : function(e){
57218         var t = Roo.lib.Event.getTarget(e);
57219         var cindex = this.view.findCellIndex(t);
57220         if(cindex !== false){
57221             return this.view.getHeaderCell(cindex);
57222         }
57223         return null;
57224     },
57225
57226     nextVisible : function(h){
57227         var v = this.view, cm = this.grid.colModel;
57228         h = h.nextSibling;
57229         while(h){
57230             if(!cm.isHidden(v.getCellIndex(h))){
57231                 return h;
57232             }
57233             h = h.nextSibling;
57234         }
57235         return null;
57236     },
57237
57238     prevVisible : function(h){
57239         var v = this.view, cm = this.grid.colModel;
57240         h = h.prevSibling;
57241         while(h){
57242             if(!cm.isHidden(v.getCellIndex(h))){
57243                 return h;
57244             }
57245             h = h.prevSibling;
57246         }
57247         return null;
57248     },
57249
57250     positionIndicator : function(h, n, e){
57251         var x = Roo.lib.Event.getPageX(e);
57252         var r = Roo.lib.Dom.getRegion(n.firstChild);
57253         var px, pt, py = r.top + this.proxyOffsets[1];
57254         if((r.right - x) <= (r.right-r.left)/2){
57255             px = r.right+this.view.borderWidth;
57256             pt = "after";
57257         }else{
57258             px = r.left;
57259             pt = "before";
57260         }
57261         var oldIndex = this.view.getCellIndex(h);
57262         var newIndex = this.view.getCellIndex(n);
57263
57264         if(this.grid.colModel.isFixed(newIndex)){
57265             return false;
57266         }
57267
57268         var locked = this.grid.colModel.isLocked(newIndex);
57269
57270         if(pt == "after"){
57271             newIndex++;
57272         }
57273         if(oldIndex < newIndex){
57274             newIndex--;
57275         }
57276         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57277             return false;
57278         }
57279         px +=  this.proxyOffsets[0];
57280         this.proxyTop.setLeftTop(px, py);
57281         this.proxyTop.show();
57282         if(!this.bottomOffset){
57283             this.bottomOffset = this.view.mainHd.getHeight();
57284         }
57285         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57286         this.proxyBottom.show();
57287         return pt;
57288     },
57289
57290     onNodeEnter : function(n, dd, e, data){
57291         if(data.header != n){
57292             this.positionIndicator(data.header, n, e);
57293         }
57294     },
57295
57296     onNodeOver : function(n, dd, e, data){
57297         var result = false;
57298         if(data.header != n){
57299             result = this.positionIndicator(data.header, n, e);
57300         }
57301         if(!result){
57302             this.proxyTop.hide();
57303             this.proxyBottom.hide();
57304         }
57305         return result ? this.dropAllowed : this.dropNotAllowed;
57306     },
57307
57308     onNodeOut : function(n, dd, e, data){
57309         this.proxyTop.hide();
57310         this.proxyBottom.hide();
57311     },
57312
57313     onNodeDrop : function(n, dd, e, data){
57314         var h = data.header;
57315         if(h != n){
57316             var cm = this.grid.colModel;
57317             var x = Roo.lib.Event.getPageX(e);
57318             var r = Roo.lib.Dom.getRegion(n.firstChild);
57319             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57320             var oldIndex = this.view.getCellIndex(h);
57321             var newIndex = this.view.getCellIndex(n);
57322             var locked = cm.isLocked(newIndex);
57323             if(pt == "after"){
57324                 newIndex++;
57325             }
57326             if(oldIndex < newIndex){
57327                 newIndex--;
57328             }
57329             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57330                 return false;
57331             }
57332             cm.setLocked(oldIndex, locked, true);
57333             cm.moveColumn(oldIndex, newIndex);
57334             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57335             return true;
57336         }
57337         return false;
57338     }
57339 });
57340 /*
57341  * Based on:
57342  * Ext JS Library 1.1.1
57343  * Copyright(c) 2006-2007, Ext JS, LLC.
57344  *
57345  * Originally Released Under LGPL - original licence link has changed is not relivant.
57346  *
57347  * Fork - LGPL
57348  * <script type="text/javascript">
57349  */
57350   
57351 /**
57352  * @class Roo.grid.GridView
57353  * @extends Roo.util.Observable
57354  *
57355  * @constructor
57356  * @param {Object} config
57357  */
57358 Roo.grid.GridView = function(config){
57359     Roo.grid.GridView.superclass.constructor.call(this);
57360     this.el = null;
57361
57362     Roo.apply(this, config);
57363 };
57364
57365 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57366
57367     unselectable :  'unselectable="on"',
57368     unselectableCls :  'x-unselectable',
57369     
57370     
57371     rowClass : "x-grid-row",
57372
57373     cellClass : "x-grid-col",
57374
57375     tdClass : "x-grid-td",
57376
57377     hdClass : "x-grid-hd",
57378
57379     splitClass : "x-grid-split",
57380
57381     sortClasses : ["sort-asc", "sort-desc"],
57382
57383     enableMoveAnim : false,
57384
57385     hlColor: "C3DAF9",
57386
57387     dh : Roo.DomHelper,
57388
57389     fly : Roo.Element.fly,
57390
57391     css : Roo.util.CSS,
57392
57393     borderWidth: 1,
57394
57395     splitOffset: 3,
57396
57397     scrollIncrement : 22,
57398
57399     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57400
57401     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57402
57403     bind : function(ds, cm){
57404         if(this.ds){
57405             this.ds.un("load", this.onLoad, this);
57406             this.ds.un("datachanged", this.onDataChange, this);
57407             this.ds.un("add", this.onAdd, this);
57408             this.ds.un("remove", this.onRemove, this);
57409             this.ds.un("update", this.onUpdate, this);
57410             this.ds.un("clear", this.onClear, this);
57411         }
57412         if(ds){
57413             ds.on("load", this.onLoad, this);
57414             ds.on("datachanged", this.onDataChange, this);
57415             ds.on("add", this.onAdd, this);
57416             ds.on("remove", this.onRemove, this);
57417             ds.on("update", this.onUpdate, this);
57418             ds.on("clear", this.onClear, this);
57419         }
57420         this.ds = ds;
57421
57422         if(this.cm){
57423             this.cm.un("widthchange", this.onColWidthChange, this);
57424             this.cm.un("headerchange", this.onHeaderChange, this);
57425             this.cm.un("hiddenchange", this.onHiddenChange, this);
57426             this.cm.un("columnmoved", this.onColumnMove, this);
57427             this.cm.un("columnlockchange", this.onColumnLock, this);
57428         }
57429         if(cm){
57430             this.generateRules(cm);
57431             cm.on("widthchange", this.onColWidthChange, this);
57432             cm.on("headerchange", this.onHeaderChange, this);
57433             cm.on("hiddenchange", this.onHiddenChange, this);
57434             cm.on("columnmoved", this.onColumnMove, this);
57435             cm.on("columnlockchange", this.onColumnLock, this);
57436         }
57437         this.cm = cm;
57438     },
57439
57440     init: function(grid){
57441         Roo.grid.GridView.superclass.init.call(this, grid);
57442
57443         this.bind(grid.dataSource, grid.colModel);
57444
57445         grid.on("headerclick", this.handleHeaderClick, this);
57446
57447         if(grid.trackMouseOver){
57448             grid.on("mouseover", this.onRowOver, this);
57449             grid.on("mouseout", this.onRowOut, this);
57450         }
57451         grid.cancelTextSelection = function(){};
57452         this.gridId = grid.id;
57453
57454         var tpls = this.templates || {};
57455
57456         if(!tpls.master){
57457             tpls.master = new Roo.Template(
57458                '<div class="x-grid" hidefocus="true">',
57459                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57460                   '<div class="x-grid-topbar"></div>',
57461                   '<div class="x-grid-scroller"><div></div></div>',
57462                   '<div class="x-grid-locked">',
57463                       '<div class="x-grid-header">{lockedHeader}</div>',
57464                       '<div class="x-grid-body">{lockedBody}</div>',
57465                   "</div>",
57466                   '<div class="x-grid-viewport">',
57467                       '<div class="x-grid-header">{header}</div>',
57468                       '<div class="x-grid-body">{body}</div>',
57469                   "</div>",
57470                   '<div class="x-grid-bottombar"></div>',
57471                  
57472                   '<div class="x-grid-resize-proxy">&#160;</div>',
57473                "</div>"
57474             );
57475             tpls.master.disableformats = true;
57476         }
57477
57478         if(!tpls.header){
57479             tpls.header = new Roo.Template(
57480                '<table border="0" cellspacing="0" cellpadding="0">',
57481                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57482                "</table>{splits}"
57483             );
57484             tpls.header.disableformats = true;
57485         }
57486         tpls.header.compile();
57487
57488         if(!tpls.hcell){
57489             tpls.hcell = new Roo.Template(
57490                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57491                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57492                 "</div></td>"
57493              );
57494              tpls.hcell.disableFormats = true;
57495         }
57496         tpls.hcell.compile();
57497
57498         if(!tpls.hsplit){
57499             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57500                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57501             tpls.hsplit.disableFormats = true;
57502         }
57503         tpls.hsplit.compile();
57504
57505         if(!tpls.body){
57506             tpls.body = new Roo.Template(
57507                '<table border="0" cellspacing="0" cellpadding="0">',
57508                "<tbody>{rows}</tbody>",
57509                "</table>"
57510             );
57511             tpls.body.disableFormats = true;
57512         }
57513         tpls.body.compile();
57514
57515         if(!tpls.row){
57516             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57517             tpls.row.disableFormats = true;
57518         }
57519         tpls.row.compile();
57520
57521         if(!tpls.cell){
57522             tpls.cell = new Roo.Template(
57523                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57524                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57525                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57526                 "</td>"
57527             );
57528             tpls.cell.disableFormats = true;
57529         }
57530         tpls.cell.compile();
57531
57532         this.templates = tpls;
57533     },
57534
57535     // remap these for backwards compat
57536     onColWidthChange : function(){
57537         this.updateColumns.apply(this, arguments);
57538     },
57539     onHeaderChange : function(){
57540         this.updateHeaders.apply(this, arguments);
57541     }, 
57542     onHiddenChange : function(){
57543         this.handleHiddenChange.apply(this, arguments);
57544     },
57545     onColumnMove : function(){
57546         this.handleColumnMove.apply(this, arguments);
57547     },
57548     onColumnLock : function(){
57549         this.handleLockChange.apply(this, arguments);
57550     },
57551
57552     onDataChange : function(){
57553         this.refresh();
57554         this.updateHeaderSortState();
57555     },
57556
57557     onClear : function(){
57558         this.refresh();
57559     },
57560
57561     onUpdate : function(ds, record){
57562         this.refreshRow(record);
57563     },
57564
57565     refreshRow : function(record){
57566         var ds = this.ds, index;
57567         if(typeof record == 'number'){
57568             index = record;
57569             record = ds.getAt(index);
57570         }else{
57571             index = ds.indexOf(record);
57572         }
57573         this.insertRows(ds, index, index, true);
57574         this.onRemove(ds, record, index+1, true);
57575         this.syncRowHeights(index, index);
57576         this.layout();
57577         this.fireEvent("rowupdated", this, index, record);
57578     },
57579
57580     onAdd : function(ds, records, index){
57581         this.insertRows(ds, index, index + (records.length-1));
57582     },
57583
57584     onRemove : function(ds, record, index, isUpdate){
57585         if(isUpdate !== true){
57586             this.fireEvent("beforerowremoved", this, index, record);
57587         }
57588         var bt = this.getBodyTable(), lt = this.getLockedTable();
57589         if(bt.rows[index]){
57590             bt.firstChild.removeChild(bt.rows[index]);
57591         }
57592         if(lt.rows[index]){
57593             lt.firstChild.removeChild(lt.rows[index]);
57594         }
57595         if(isUpdate !== true){
57596             this.stripeRows(index);
57597             this.syncRowHeights(index, index);
57598             this.layout();
57599             this.fireEvent("rowremoved", this, index, record);
57600         }
57601     },
57602
57603     onLoad : function(){
57604         this.scrollToTop();
57605     },
57606
57607     /**
57608      * Scrolls the grid to the top
57609      */
57610     scrollToTop : function(){
57611         if(this.scroller){
57612             this.scroller.dom.scrollTop = 0;
57613             this.syncScroll();
57614         }
57615     },
57616
57617     /**
57618      * Gets a panel in the header of the grid that can be used for toolbars etc.
57619      * After modifying the contents of this panel a call to grid.autoSize() may be
57620      * required to register any changes in size.
57621      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57622      * @return Roo.Element
57623      */
57624     getHeaderPanel : function(doShow){
57625         if(doShow){
57626             this.headerPanel.show();
57627         }
57628         return this.headerPanel;
57629     },
57630
57631     /**
57632      * Gets a panel in the footer of the grid that can be used for toolbars etc.
57633      * After modifying the contents of this panel a call to grid.autoSize() may be
57634      * required to register any changes in size.
57635      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
57636      * @return Roo.Element
57637      */
57638     getFooterPanel : function(doShow){
57639         if(doShow){
57640             this.footerPanel.show();
57641         }
57642         return this.footerPanel;
57643     },
57644
57645     initElements : function(){
57646         var E = Roo.Element;
57647         var el = this.grid.getGridEl().dom.firstChild;
57648         var cs = el.childNodes;
57649
57650         this.el = new E(el);
57651         
57652          this.focusEl = new E(el.firstChild);
57653         this.focusEl.swallowEvent("click", true);
57654         
57655         this.headerPanel = new E(cs[1]);
57656         this.headerPanel.enableDisplayMode("block");
57657
57658         this.scroller = new E(cs[2]);
57659         this.scrollSizer = new E(this.scroller.dom.firstChild);
57660
57661         this.lockedWrap = new E(cs[3]);
57662         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
57663         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
57664
57665         this.mainWrap = new E(cs[4]);
57666         this.mainHd = new E(this.mainWrap.dom.firstChild);
57667         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
57668
57669         this.footerPanel = new E(cs[5]);
57670         this.footerPanel.enableDisplayMode("block");
57671
57672         this.resizeProxy = new E(cs[6]);
57673
57674         this.headerSelector = String.format(
57675            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
57676            this.lockedHd.id, this.mainHd.id
57677         );
57678
57679         this.splitterSelector = String.format(
57680            '#{0} div.x-grid-split, #{1} div.x-grid-split',
57681            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
57682         );
57683     },
57684     idToCssName : function(s)
57685     {
57686         return s.replace(/[^a-z0-9]+/ig, '-');
57687     },
57688
57689     getHeaderCell : function(index){
57690         return Roo.DomQuery.select(this.headerSelector)[index];
57691     },
57692
57693     getHeaderCellMeasure : function(index){
57694         return this.getHeaderCell(index).firstChild;
57695     },
57696
57697     getHeaderCellText : function(index){
57698         return this.getHeaderCell(index).firstChild.firstChild;
57699     },
57700
57701     getLockedTable : function(){
57702         return this.lockedBody.dom.firstChild;
57703     },
57704
57705     getBodyTable : function(){
57706         return this.mainBody.dom.firstChild;
57707     },
57708
57709     getLockedRow : function(index){
57710         return this.getLockedTable().rows[index];
57711     },
57712
57713     getRow : function(index){
57714         return this.getBodyTable().rows[index];
57715     },
57716
57717     getRowComposite : function(index){
57718         if(!this.rowEl){
57719             this.rowEl = new Roo.CompositeElementLite();
57720         }
57721         var els = [], lrow, mrow;
57722         if(lrow = this.getLockedRow(index)){
57723             els.push(lrow);
57724         }
57725         if(mrow = this.getRow(index)){
57726             els.push(mrow);
57727         }
57728         this.rowEl.elements = els;
57729         return this.rowEl;
57730     },
57731     /**
57732      * Gets the 'td' of the cell
57733      * 
57734      * @param {Integer} rowIndex row to select
57735      * @param {Integer} colIndex column to select
57736      * 
57737      * @return {Object} 
57738      */
57739     getCell : function(rowIndex, colIndex){
57740         var locked = this.cm.getLockedCount();
57741         var source;
57742         if(colIndex < locked){
57743             source = this.lockedBody.dom.firstChild;
57744         }else{
57745             source = this.mainBody.dom.firstChild;
57746             colIndex -= locked;
57747         }
57748         return source.rows[rowIndex].childNodes[colIndex];
57749     },
57750
57751     getCellText : function(rowIndex, colIndex){
57752         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
57753     },
57754
57755     getCellBox : function(cell){
57756         var b = this.fly(cell).getBox();
57757         if(Roo.isOpera){ // opera fails to report the Y
57758             b.y = cell.offsetTop + this.mainBody.getY();
57759         }
57760         return b;
57761     },
57762
57763     getCellIndex : function(cell){
57764         var id = String(cell.className).match(this.cellRE);
57765         if(id){
57766             return parseInt(id[1], 10);
57767         }
57768         return 0;
57769     },
57770
57771     findHeaderIndex : function(n){
57772         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57773         return r ? this.getCellIndex(r) : false;
57774     },
57775
57776     findHeaderCell : function(n){
57777         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
57778         return r ? r : false;
57779     },
57780
57781     findRowIndex : function(n){
57782         if(!n){
57783             return false;
57784         }
57785         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
57786         return r ? r.rowIndex : false;
57787     },
57788
57789     findCellIndex : function(node){
57790         var stop = this.el.dom;
57791         while(node && node != stop){
57792             if(this.findRE.test(node.className)){
57793                 return this.getCellIndex(node);
57794             }
57795             node = node.parentNode;
57796         }
57797         return false;
57798     },
57799
57800     getColumnId : function(index){
57801         return this.cm.getColumnId(index);
57802     },
57803
57804     getSplitters : function()
57805     {
57806         if(this.splitterSelector){
57807            return Roo.DomQuery.select(this.splitterSelector);
57808         }else{
57809             return null;
57810       }
57811     },
57812
57813     getSplitter : function(index){
57814         return this.getSplitters()[index];
57815     },
57816
57817     onRowOver : function(e, t){
57818         var row;
57819         if((row = this.findRowIndex(t)) !== false){
57820             this.getRowComposite(row).addClass("x-grid-row-over");
57821         }
57822     },
57823
57824     onRowOut : function(e, t){
57825         var row;
57826         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
57827             this.getRowComposite(row).removeClass("x-grid-row-over");
57828         }
57829     },
57830
57831     renderHeaders : function(){
57832         var cm = this.cm;
57833         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
57834         var cb = [], lb = [], sb = [], lsb = [], p = {};
57835         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57836             p.cellId = "x-grid-hd-0-" + i;
57837             p.splitId = "x-grid-csplit-0-" + i;
57838             p.id = cm.getColumnId(i);
57839             p.value = cm.getColumnHeader(i) || "";
57840             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
57841             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
57842             if(!cm.isLocked(i)){
57843                 cb[cb.length] = ct.apply(p);
57844                 sb[sb.length] = st.apply(p);
57845             }else{
57846                 lb[lb.length] = ct.apply(p);
57847                 lsb[lsb.length] = st.apply(p);
57848             }
57849         }
57850         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
57851                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
57852     },
57853
57854     updateHeaders : function(){
57855         var html = this.renderHeaders();
57856         this.lockedHd.update(html[0]);
57857         this.mainHd.update(html[1]);
57858     },
57859
57860     /**
57861      * Focuses the specified row.
57862      * @param {Number} row The row index
57863      */
57864     focusRow : function(row)
57865     {
57866         //Roo.log('GridView.focusRow');
57867         var x = this.scroller.dom.scrollLeft;
57868         this.focusCell(row, 0, false);
57869         this.scroller.dom.scrollLeft = x;
57870     },
57871
57872     /**
57873      * Focuses the specified cell.
57874      * @param {Number} row The row index
57875      * @param {Number} col The column index
57876      * @param {Boolean} hscroll false to disable horizontal scrolling
57877      */
57878     focusCell : function(row, col, hscroll)
57879     {
57880         //Roo.log('GridView.focusCell');
57881         var el = this.ensureVisible(row, col, hscroll);
57882         this.focusEl.alignTo(el, "tl-tl");
57883         if(Roo.isGecko){
57884             this.focusEl.focus();
57885         }else{
57886             this.focusEl.focus.defer(1, this.focusEl);
57887         }
57888     },
57889
57890     /**
57891      * Scrolls the specified cell into view
57892      * @param {Number} row The row index
57893      * @param {Number} col The column index
57894      * @param {Boolean} hscroll false to disable horizontal scrolling
57895      */
57896     ensureVisible : function(row, col, hscroll)
57897     {
57898         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
57899         //return null; //disable for testing.
57900         if(typeof row != "number"){
57901             row = row.rowIndex;
57902         }
57903         if(row < 0 && row >= this.ds.getCount()){
57904             return  null;
57905         }
57906         col = (col !== undefined ? col : 0);
57907         var cm = this.grid.colModel;
57908         while(cm.isHidden(col)){
57909             col++;
57910         }
57911
57912         var el = this.getCell(row, col);
57913         if(!el){
57914             return null;
57915         }
57916         var c = this.scroller.dom;
57917
57918         var ctop = parseInt(el.offsetTop, 10);
57919         var cleft = parseInt(el.offsetLeft, 10);
57920         var cbot = ctop + el.offsetHeight;
57921         var cright = cleft + el.offsetWidth;
57922         
57923         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
57924         var stop = parseInt(c.scrollTop, 10);
57925         var sleft = parseInt(c.scrollLeft, 10);
57926         var sbot = stop + ch;
57927         var sright = sleft + c.clientWidth;
57928         /*
57929         Roo.log('GridView.ensureVisible:' +
57930                 ' ctop:' + ctop +
57931                 ' c.clientHeight:' + c.clientHeight +
57932                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
57933                 ' stop:' + stop +
57934                 ' cbot:' + cbot +
57935                 ' sbot:' + sbot +
57936                 ' ch:' + ch  
57937                 );
57938         */
57939         if(ctop < stop){
57940             c.scrollTop = ctop;
57941             //Roo.log("set scrolltop to ctop DISABLE?");
57942         }else if(cbot > sbot){
57943             //Roo.log("set scrolltop to cbot-ch");
57944             c.scrollTop = cbot-ch;
57945         }
57946         
57947         if(hscroll !== false){
57948             if(cleft < sleft){
57949                 c.scrollLeft = cleft;
57950             }else if(cright > sright){
57951                 c.scrollLeft = cright-c.clientWidth;
57952             }
57953         }
57954          
57955         return el;
57956     },
57957
57958     updateColumns : function(){
57959         this.grid.stopEditing();
57960         var cm = this.grid.colModel, colIds = this.getColumnIds();
57961         //var totalWidth = cm.getTotalWidth();
57962         var pos = 0;
57963         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57964             //if(cm.isHidden(i)) continue;
57965             var w = cm.getColumnWidth(i);
57966             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57967             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
57968         }
57969         this.updateSplitters();
57970     },
57971
57972     generateRules : function(cm){
57973         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
57974         Roo.util.CSS.removeStyleSheet(rulesId);
57975         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57976             var cid = cm.getColumnId(i);
57977             var align = '';
57978             if(cm.config[i].align){
57979                 align = 'text-align:'+cm.config[i].align+';';
57980             }
57981             var hidden = '';
57982             if(cm.isHidden(i)){
57983                 hidden = 'display:none;';
57984             }
57985             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
57986             ruleBuf.push(
57987                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
57988                     this.hdSelector, cid, " {\n", align, width, "}\n",
57989                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
57990                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
57991         }
57992         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57993     },
57994
57995     updateSplitters : function(){
57996         var cm = this.cm, s = this.getSplitters();
57997         if(s){ // splitters not created yet
57998             var pos = 0, locked = true;
57999             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58000                 if(cm.isHidden(i)) {
58001                     continue;
58002                 }
58003                 var w = cm.getColumnWidth(i); // make sure it's a number
58004                 if(!cm.isLocked(i) && locked){
58005                     pos = 0;
58006                     locked = false;
58007                 }
58008                 pos += w;
58009                 s[i].style.left = (pos-this.splitOffset) + "px";
58010             }
58011         }
58012     },
58013
58014     handleHiddenChange : function(colModel, colIndex, hidden){
58015         if(hidden){
58016             this.hideColumn(colIndex);
58017         }else{
58018             this.unhideColumn(colIndex);
58019         }
58020     },
58021
58022     hideColumn : function(colIndex){
58023         var cid = this.getColumnId(colIndex);
58024         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58025         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58026         if(Roo.isSafari){
58027             this.updateHeaders();
58028         }
58029         this.updateSplitters();
58030         this.layout();
58031     },
58032
58033     unhideColumn : function(colIndex){
58034         var cid = this.getColumnId(colIndex);
58035         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58036         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58037
58038         if(Roo.isSafari){
58039             this.updateHeaders();
58040         }
58041         this.updateSplitters();
58042         this.layout();
58043     },
58044
58045     insertRows : function(dm, firstRow, lastRow, isUpdate){
58046         if(firstRow == 0 && lastRow == dm.getCount()-1){
58047             this.refresh();
58048         }else{
58049             if(!isUpdate){
58050                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58051             }
58052             var s = this.getScrollState();
58053             var markup = this.renderRows(firstRow, lastRow);
58054             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58055             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58056             this.restoreScroll(s);
58057             if(!isUpdate){
58058                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58059                 this.syncRowHeights(firstRow, lastRow);
58060                 this.stripeRows(firstRow);
58061                 this.layout();
58062             }
58063         }
58064     },
58065
58066     bufferRows : function(markup, target, index){
58067         var before = null, trows = target.rows, tbody = target.tBodies[0];
58068         if(index < trows.length){
58069             before = trows[index];
58070         }
58071         var b = document.createElement("div");
58072         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58073         var rows = b.firstChild.rows;
58074         for(var i = 0, len = rows.length; i < len; i++){
58075             if(before){
58076                 tbody.insertBefore(rows[0], before);
58077             }else{
58078                 tbody.appendChild(rows[0]);
58079             }
58080         }
58081         b.innerHTML = "";
58082         b = null;
58083     },
58084
58085     deleteRows : function(dm, firstRow, lastRow){
58086         if(dm.getRowCount()<1){
58087             this.fireEvent("beforerefresh", this);
58088             this.mainBody.update("");
58089             this.lockedBody.update("");
58090             this.fireEvent("refresh", this);
58091         }else{
58092             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58093             var bt = this.getBodyTable();
58094             var tbody = bt.firstChild;
58095             var rows = bt.rows;
58096             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58097                 tbody.removeChild(rows[firstRow]);
58098             }
58099             this.stripeRows(firstRow);
58100             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58101         }
58102     },
58103
58104     updateRows : function(dataSource, firstRow, lastRow){
58105         var s = this.getScrollState();
58106         this.refresh();
58107         this.restoreScroll(s);
58108     },
58109
58110     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58111         if(!noRefresh){
58112            this.refresh();
58113         }
58114         this.updateHeaderSortState();
58115     },
58116
58117     getScrollState : function(){
58118         
58119         var sb = this.scroller.dom;
58120         return {left: sb.scrollLeft, top: sb.scrollTop};
58121     },
58122
58123     stripeRows : function(startRow){
58124         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58125             return;
58126         }
58127         startRow = startRow || 0;
58128         var rows = this.getBodyTable().rows;
58129         var lrows = this.getLockedTable().rows;
58130         var cls = ' x-grid-row-alt ';
58131         for(var i = startRow, len = rows.length; i < len; i++){
58132             var row = rows[i], lrow = lrows[i];
58133             var isAlt = ((i+1) % 2 == 0);
58134             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58135             if(isAlt == hasAlt){
58136                 continue;
58137             }
58138             if(isAlt){
58139                 row.className += " x-grid-row-alt";
58140             }else{
58141                 row.className = row.className.replace("x-grid-row-alt", "");
58142             }
58143             if(lrow){
58144                 lrow.className = row.className;
58145             }
58146         }
58147     },
58148
58149     restoreScroll : function(state){
58150         //Roo.log('GridView.restoreScroll');
58151         var sb = this.scroller.dom;
58152         sb.scrollLeft = state.left;
58153         sb.scrollTop = state.top;
58154         this.syncScroll();
58155     },
58156
58157     syncScroll : function(){
58158         //Roo.log('GridView.syncScroll');
58159         var sb = this.scroller.dom;
58160         var sh = this.mainHd.dom;
58161         var bs = this.mainBody.dom;
58162         var lv = this.lockedBody.dom;
58163         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58164         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58165     },
58166
58167     handleScroll : function(e){
58168         this.syncScroll();
58169         var sb = this.scroller.dom;
58170         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58171         e.stopEvent();
58172     },
58173
58174     handleWheel : function(e){
58175         var d = e.getWheelDelta();
58176         this.scroller.dom.scrollTop -= d*22;
58177         // set this here to prevent jumpy scrolling on large tables
58178         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
58179         e.stopEvent();
58180     },
58181
58182     renderRows : function(startRow, endRow){
58183         // pull in all the crap needed to render rows
58184         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
58185         var colCount = cm.getColumnCount();
58186
58187         if(ds.getCount() < 1){
58188             return ["", ""];
58189         }
58190
58191         // build a map for all the columns
58192         var cs = [];
58193         for(var i = 0; i < colCount; i++){
58194             var name = cm.getDataIndex(i);
58195             cs[i] = {
58196                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58197                 renderer : cm.getRenderer(i),
58198                 id : cm.getColumnId(i),
58199                 locked : cm.isLocked(i),
58200                 has_editor : cm.isCellEditable(i)
58201             };
58202         }
58203
58204         startRow = startRow || 0;
58205         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58206
58207         // records to render
58208         var rs = ds.getRange(startRow, endRow);
58209
58210         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58211     },
58212
58213     // As much as I hate to duplicate code, this was branched because FireFox really hates
58214     // [].join("") on strings. The performance difference was substantial enough to
58215     // branch this function
58216     doRender : Roo.isGecko ?
58217             function(cs, rs, ds, startRow, colCount, stripe){
58218                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58219                 // buffers
58220                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58221                 
58222                 var hasListener = this.grid.hasListener('rowclass');
58223                 var rowcfg = {};
58224                 for(var j = 0, len = rs.length; j < len; j++){
58225                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58226                     for(var i = 0; i < colCount; i++){
58227                         c = cs[i];
58228                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58229                         p.id = c.id;
58230                         p.css = p.attr = "";
58231                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58232                         if(p.value == undefined || p.value === "") {
58233                             p.value = "&#160;";
58234                         }
58235                         if(c.has_editor){
58236                             p.css += ' x-grid-editable-cell';
58237                         }
58238                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58239                             p.css +=  ' x-grid-dirty-cell';
58240                         }
58241                         var markup = ct.apply(p);
58242                         if(!c.locked){
58243                             cb+= markup;
58244                         }else{
58245                             lcb+= markup;
58246                         }
58247                     }
58248                     var alt = [];
58249                     if(stripe && ((rowIndex+1) % 2 == 0)){
58250                         alt.push("x-grid-row-alt")
58251                     }
58252                     if(r.dirty){
58253                         alt.push(  " x-grid-dirty-row");
58254                     }
58255                     rp.cells = lcb;
58256                     if(this.getRowClass){
58257                         alt.push(this.getRowClass(r, rowIndex));
58258                     }
58259                     if (hasListener) {
58260                         rowcfg = {
58261                              
58262                             record: r,
58263                             rowIndex : rowIndex,
58264                             rowClass : ''
58265                         };
58266                         this.grid.fireEvent('rowclass', this, rowcfg);
58267                         alt.push(rowcfg.rowClass);
58268                     }
58269                     rp.alt = alt.join(" ");
58270                     lbuf+= rt.apply(rp);
58271                     rp.cells = cb;
58272                     buf+=  rt.apply(rp);
58273                 }
58274                 return [lbuf, buf];
58275             } :
58276             function(cs, rs, ds, startRow, colCount, stripe){
58277                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58278                 // buffers
58279                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58280                 var hasListener = this.grid.hasListener('rowclass');
58281  
58282                 var rowcfg = {};
58283                 for(var j = 0, len = rs.length; j < len; j++){
58284                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58285                     for(var i = 0; i < colCount; i++){
58286                         c = cs[i];
58287                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58288                         p.id = c.id;
58289                         p.css = p.attr = "";
58290                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58291                         if(p.value == undefined || p.value === "") {
58292                             p.value = "&#160;";
58293                         }
58294                         //Roo.log(c);
58295                          if(c.has_editor){
58296                             p.css += ' x-grid-editable-cell';
58297                         }
58298                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58299                             p.css += ' x-grid-dirty-cell' 
58300                         }
58301                         
58302                         var markup = ct.apply(p);
58303                         if(!c.locked){
58304                             cb[cb.length] = markup;
58305                         }else{
58306                             lcb[lcb.length] = markup;
58307                         }
58308                     }
58309                     var alt = [];
58310                     if(stripe && ((rowIndex+1) % 2 == 0)){
58311                         alt.push( "x-grid-row-alt");
58312                     }
58313                     if(r.dirty){
58314                         alt.push(" x-grid-dirty-row");
58315                     }
58316                     rp.cells = lcb;
58317                     if(this.getRowClass){
58318                         alt.push( this.getRowClass(r, rowIndex));
58319                     }
58320                     if (hasListener) {
58321                         rowcfg = {
58322                              
58323                             record: r,
58324                             rowIndex : rowIndex,
58325                             rowClass : ''
58326                         };
58327                         this.grid.fireEvent('rowclass', this, rowcfg);
58328                         alt.push(rowcfg.rowClass);
58329                     }
58330                     
58331                     rp.alt = alt.join(" ");
58332                     rp.cells = lcb.join("");
58333                     lbuf[lbuf.length] = rt.apply(rp);
58334                     rp.cells = cb.join("");
58335                     buf[buf.length] =  rt.apply(rp);
58336                 }
58337                 return [lbuf.join(""), buf.join("")];
58338             },
58339
58340     renderBody : function(){
58341         var markup = this.renderRows();
58342         var bt = this.templates.body;
58343         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58344     },
58345
58346     /**
58347      * Refreshes the grid
58348      * @param {Boolean} headersToo
58349      */
58350     refresh : function(headersToo){
58351         this.fireEvent("beforerefresh", this);
58352         this.grid.stopEditing();
58353         var result = this.renderBody();
58354         this.lockedBody.update(result[0]);
58355         this.mainBody.update(result[1]);
58356         if(headersToo === true){
58357             this.updateHeaders();
58358             this.updateColumns();
58359             this.updateSplitters();
58360             this.updateHeaderSortState();
58361         }
58362         this.syncRowHeights();
58363         this.layout();
58364         this.fireEvent("refresh", this);
58365     },
58366
58367     handleColumnMove : function(cm, oldIndex, newIndex){
58368         this.indexMap = null;
58369         var s = this.getScrollState();
58370         this.refresh(true);
58371         this.restoreScroll(s);
58372         this.afterMove(newIndex);
58373     },
58374
58375     afterMove : function(colIndex){
58376         if(this.enableMoveAnim && Roo.enableFx){
58377             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58378         }
58379         // if multisort - fix sortOrder, and reload..
58380         if (this.grid.dataSource.multiSort) {
58381             // the we can call sort again..
58382             var dm = this.grid.dataSource;
58383             var cm = this.grid.colModel;
58384             var so = [];
58385             for(var i = 0; i < cm.config.length; i++ ) {
58386                 
58387                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58388                     continue; // dont' bother, it's not in sort list or being set.
58389                 }
58390                 
58391                 so.push(cm.config[i].dataIndex);
58392             };
58393             dm.sortOrder = so;
58394             dm.load(dm.lastOptions);
58395             
58396             
58397         }
58398         
58399     },
58400
58401     updateCell : function(dm, rowIndex, dataIndex){
58402         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58403         if(typeof colIndex == "undefined"){ // not present in grid
58404             return;
58405         }
58406         var cm = this.grid.colModel;
58407         var cell = this.getCell(rowIndex, colIndex);
58408         var cellText = this.getCellText(rowIndex, colIndex);
58409
58410         var p = {
58411             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58412             id : cm.getColumnId(colIndex),
58413             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58414         };
58415         var renderer = cm.getRenderer(colIndex);
58416         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58417         if(typeof val == "undefined" || val === "") {
58418             val = "&#160;";
58419         }
58420         cellText.innerHTML = val;
58421         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58422         this.syncRowHeights(rowIndex, rowIndex);
58423     },
58424
58425     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58426         var maxWidth = 0;
58427         if(this.grid.autoSizeHeaders){
58428             var h = this.getHeaderCellMeasure(colIndex);
58429             maxWidth = Math.max(maxWidth, h.scrollWidth);
58430         }
58431         var tb, index;
58432         if(this.cm.isLocked(colIndex)){
58433             tb = this.getLockedTable();
58434             index = colIndex;
58435         }else{
58436             tb = this.getBodyTable();
58437             index = colIndex - this.cm.getLockedCount();
58438         }
58439         if(tb && tb.rows){
58440             var rows = tb.rows;
58441             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58442             for(var i = 0; i < stopIndex; i++){
58443                 var cell = rows[i].childNodes[index].firstChild;
58444                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58445             }
58446         }
58447         return maxWidth + /*margin for error in IE*/ 5;
58448     },
58449     /**
58450      * Autofit a column to its content.
58451      * @param {Number} colIndex
58452      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58453      */
58454      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58455          if(this.cm.isHidden(colIndex)){
58456              return; // can't calc a hidden column
58457          }
58458         if(forceMinSize){
58459             var cid = this.cm.getColumnId(colIndex);
58460             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58461            if(this.grid.autoSizeHeaders){
58462                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58463            }
58464         }
58465         var newWidth = this.calcColumnWidth(colIndex);
58466         this.cm.setColumnWidth(colIndex,
58467             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58468         if(!suppressEvent){
58469             this.grid.fireEvent("columnresize", colIndex, newWidth);
58470         }
58471     },
58472
58473     /**
58474      * Autofits all columns to their content and then expands to fit any extra space in the grid
58475      */
58476      autoSizeColumns : function(){
58477         var cm = this.grid.colModel;
58478         var colCount = cm.getColumnCount();
58479         for(var i = 0; i < colCount; i++){
58480             this.autoSizeColumn(i, true, true);
58481         }
58482         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58483             this.fitColumns();
58484         }else{
58485             this.updateColumns();
58486             this.layout();
58487         }
58488     },
58489
58490     /**
58491      * Autofits all columns to the grid's width proportionate with their current size
58492      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58493      */
58494     fitColumns : function(reserveScrollSpace){
58495         var cm = this.grid.colModel;
58496         var colCount = cm.getColumnCount();
58497         var cols = [];
58498         var width = 0;
58499         var i, w;
58500         for (i = 0; i < colCount; i++){
58501             if(!cm.isHidden(i) && !cm.isFixed(i)){
58502                 w = cm.getColumnWidth(i);
58503                 cols.push(i);
58504                 cols.push(w);
58505                 width += w;
58506             }
58507         }
58508         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58509         if(reserveScrollSpace){
58510             avail -= 17;
58511         }
58512         var frac = (avail - cm.getTotalWidth())/width;
58513         while (cols.length){
58514             w = cols.pop();
58515             i = cols.pop();
58516             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58517         }
58518         this.updateColumns();
58519         this.layout();
58520     },
58521
58522     onRowSelect : function(rowIndex){
58523         var row = this.getRowComposite(rowIndex);
58524         row.addClass("x-grid-row-selected");
58525     },
58526
58527     onRowDeselect : function(rowIndex){
58528         var row = this.getRowComposite(rowIndex);
58529         row.removeClass("x-grid-row-selected");
58530     },
58531
58532     onCellSelect : function(row, col){
58533         var cell = this.getCell(row, col);
58534         if(cell){
58535             Roo.fly(cell).addClass("x-grid-cell-selected");
58536         }
58537     },
58538
58539     onCellDeselect : function(row, col){
58540         var cell = this.getCell(row, col);
58541         if(cell){
58542             Roo.fly(cell).removeClass("x-grid-cell-selected");
58543         }
58544     },
58545
58546     updateHeaderSortState : function(){
58547         
58548         // sort state can be single { field: xxx, direction : yyy}
58549         // or   { xxx=>ASC , yyy : DESC ..... }
58550         
58551         var mstate = {};
58552         if (!this.ds.multiSort) { 
58553             var state = this.ds.getSortState();
58554             if(!state){
58555                 return;
58556             }
58557             mstate[state.field] = state.direction;
58558             // FIXME... - this is not used here.. but might be elsewhere..
58559             this.sortState = state;
58560             
58561         } else {
58562             mstate = this.ds.sortToggle;
58563         }
58564         //remove existing sort classes..
58565         
58566         var sc = this.sortClasses;
58567         var hds = this.el.select(this.headerSelector).removeClass(sc);
58568         
58569         for(var f in mstate) {
58570         
58571             var sortColumn = this.cm.findColumnIndex(f);
58572             
58573             if(sortColumn != -1){
58574                 var sortDir = mstate[f];        
58575                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58576             }
58577         }
58578         
58579          
58580         
58581     },
58582
58583
58584     handleHeaderClick : function(g, index,e){
58585         
58586         Roo.log("header click");
58587         
58588         if (Roo.isTouch) {
58589             // touch events on header are handled by context
58590             this.handleHdCtx(g,index,e);
58591             return;
58592         }
58593         
58594         
58595         if(this.headersDisabled){
58596             return;
58597         }
58598         var dm = g.dataSource, cm = g.colModel;
58599         if(!cm.isSortable(index)){
58600             return;
58601         }
58602         g.stopEditing();
58603         
58604         if (dm.multiSort) {
58605             // update the sortOrder
58606             var so = [];
58607             for(var i = 0; i < cm.config.length; i++ ) {
58608                 
58609                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58610                     continue; // dont' bother, it's not in sort list or being set.
58611                 }
58612                 
58613                 so.push(cm.config[i].dataIndex);
58614             };
58615             dm.sortOrder = so;
58616         }
58617         
58618         
58619         dm.sort(cm.getDataIndex(index));
58620     },
58621
58622
58623     destroy : function(){
58624         if(this.colMenu){
58625             this.colMenu.removeAll();
58626             Roo.menu.MenuMgr.unregister(this.colMenu);
58627             this.colMenu.getEl().remove();
58628             delete this.colMenu;
58629         }
58630         if(this.hmenu){
58631             this.hmenu.removeAll();
58632             Roo.menu.MenuMgr.unregister(this.hmenu);
58633             this.hmenu.getEl().remove();
58634             delete this.hmenu;
58635         }
58636         if(this.grid.enableColumnMove){
58637             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58638             if(dds){
58639                 for(var dd in dds){
58640                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
58641                         var elid = dds[dd].dragElId;
58642                         dds[dd].unreg();
58643                         Roo.get(elid).remove();
58644                     } else if(dds[dd].config.isTarget){
58645                         dds[dd].proxyTop.remove();
58646                         dds[dd].proxyBottom.remove();
58647                         dds[dd].unreg();
58648                     }
58649                     if(Roo.dd.DDM.locationCache[dd]){
58650                         delete Roo.dd.DDM.locationCache[dd];
58651                     }
58652                 }
58653                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58654             }
58655         }
58656         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
58657         this.bind(null, null);
58658         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
58659     },
58660
58661     handleLockChange : function(){
58662         this.refresh(true);
58663     },
58664
58665     onDenyColumnLock : function(){
58666
58667     },
58668
58669     onDenyColumnHide : function(){
58670
58671     },
58672
58673     handleHdMenuClick : function(item){
58674         var index = this.hdCtxIndex;
58675         var cm = this.cm, ds = this.ds;
58676         switch(item.id){
58677             case "asc":
58678                 ds.sort(cm.getDataIndex(index), "ASC");
58679                 break;
58680             case "desc":
58681                 ds.sort(cm.getDataIndex(index), "DESC");
58682                 break;
58683             case "lock":
58684                 var lc = cm.getLockedCount();
58685                 if(cm.getColumnCount(true) <= lc+1){
58686                     this.onDenyColumnLock();
58687                     return;
58688                 }
58689                 if(lc != index){
58690                     cm.setLocked(index, true, true);
58691                     cm.moveColumn(index, lc);
58692                     this.grid.fireEvent("columnmove", index, lc);
58693                 }else{
58694                     cm.setLocked(index, true);
58695                 }
58696             break;
58697             case "unlock":
58698                 var lc = cm.getLockedCount();
58699                 if((lc-1) != index){
58700                     cm.setLocked(index, false, true);
58701                     cm.moveColumn(index, lc-1);
58702                     this.grid.fireEvent("columnmove", index, lc-1);
58703                 }else{
58704                     cm.setLocked(index, false);
58705                 }
58706             break;
58707             case 'wider': // used to expand cols on touch..
58708             case 'narrow':
58709                 var cw = cm.getColumnWidth(index);
58710                 cw += (item.id == 'wider' ? 1 : -1) * 50;
58711                 cw = Math.max(0, cw);
58712                 cw = Math.min(cw,4000);
58713                 cm.setColumnWidth(index, cw);
58714                 break;
58715                 
58716             default:
58717                 index = cm.getIndexById(item.id.substr(4));
58718                 if(index != -1){
58719                     if(item.checked && cm.getColumnCount(true) <= 1){
58720                         this.onDenyColumnHide();
58721                         return false;
58722                     }
58723                     cm.setHidden(index, item.checked);
58724                 }
58725         }
58726         return true;
58727     },
58728
58729     beforeColMenuShow : function(){
58730         var cm = this.cm,  colCount = cm.getColumnCount();
58731         this.colMenu.removeAll();
58732         
58733         var items = [];
58734         for(var i = 0; i < colCount; i++){
58735             items.push({
58736                 id: "col-"+cm.getColumnId(i),
58737                 text: cm.getColumnHeader(i),
58738                 checked: !cm.isHidden(i),
58739                 hideOnClick:false
58740             });
58741         }
58742         
58743         if (this.grid.sortColMenu) {
58744             items.sort(function(a,b) {
58745                 if (a.text == b.text) {
58746                     return 0;
58747                 }
58748                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
58749             });
58750         }
58751         
58752         for(var i = 0; i < colCount; i++){
58753             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
58754         }
58755     },
58756
58757     handleHdCtx : function(g, index, e){
58758         e.stopEvent();
58759         var hd = this.getHeaderCell(index);
58760         this.hdCtxIndex = index;
58761         var ms = this.hmenu.items, cm = this.cm;
58762         ms.get("asc").setDisabled(!cm.isSortable(index));
58763         ms.get("desc").setDisabled(!cm.isSortable(index));
58764         if(this.grid.enableColLock !== false){
58765             ms.get("lock").setDisabled(cm.isLocked(index));
58766             ms.get("unlock").setDisabled(!cm.isLocked(index));
58767         }
58768         this.hmenu.show(hd, "tl-bl");
58769     },
58770
58771     handleHdOver : function(e){
58772         var hd = this.findHeaderCell(e.getTarget());
58773         if(hd && !this.headersDisabled){
58774             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
58775                this.fly(hd).addClass("x-grid-hd-over");
58776             }
58777         }
58778     },
58779
58780     handleHdOut : function(e){
58781         var hd = this.findHeaderCell(e.getTarget());
58782         if(hd){
58783             this.fly(hd).removeClass("x-grid-hd-over");
58784         }
58785     },
58786
58787     handleSplitDblClick : function(e, t){
58788         var i = this.getCellIndex(t);
58789         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
58790             this.autoSizeColumn(i, true);
58791             this.layout();
58792         }
58793     },
58794
58795     render : function(){
58796
58797         var cm = this.cm;
58798         var colCount = cm.getColumnCount();
58799
58800         if(this.grid.monitorWindowResize === true){
58801             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58802         }
58803         var header = this.renderHeaders();
58804         var body = this.templates.body.apply({rows:""});
58805         var html = this.templates.master.apply({
58806             lockedBody: body,
58807             body: body,
58808             lockedHeader: header[0],
58809             header: header[1]
58810         });
58811
58812         //this.updateColumns();
58813
58814         this.grid.getGridEl().dom.innerHTML = html;
58815
58816         this.initElements();
58817         
58818         // a kludge to fix the random scolling effect in webkit
58819         this.el.on("scroll", function() {
58820             this.el.dom.scrollTop=0; // hopefully not recursive..
58821         },this);
58822
58823         this.scroller.on("scroll", this.handleScroll, this);
58824         this.lockedBody.on("mousewheel", this.handleWheel, this);
58825         this.mainBody.on("mousewheel", this.handleWheel, this);
58826
58827         this.mainHd.on("mouseover", this.handleHdOver, this);
58828         this.mainHd.on("mouseout", this.handleHdOut, this);
58829         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
58830                 {delegate: "."+this.splitClass});
58831
58832         this.lockedHd.on("mouseover", this.handleHdOver, this);
58833         this.lockedHd.on("mouseout", this.handleHdOut, this);
58834         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
58835                 {delegate: "."+this.splitClass});
58836
58837         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
58838             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58839         }
58840
58841         this.updateSplitters();
58842
58843         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
58844             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58845             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
58846         }
58847
58848         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
58849             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
58850             this.hmenu.add(
58851                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
58852                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
58853             );
58854             if(this.grid.enableColLock !== false){
58855                 this.hmenu.add('-',
58856                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
58857                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
58858                 );
58859             }
58860             if (Roo.isTouch) {
58861                  this.hmenu.add('-',
58862                     {id:"wider", text: this.columnsWiderText},
58863                     {id:"narrow", text: this.columnsNarrowText }
58864                 );
58865                 
58866                  
58867             }
58868             
58869             if(this.grid.enableColumnHide !== false){
58870
58871                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
58872                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
58873                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
58874
58875                 this.hmenu.add('-',
58876                     {id:"columns", text: this.columnsText, menu: this.colMenu}
58877                 );
58878             }
58879             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
58880
58881             this.grid.on("headercontextmenu", this.handleHdCtx, this);
58882         }
58883
58884         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
58885             this.dd = new Roo.grid.GridDragZone(this.grid, {
58886                 ddGroup : this.grid.ddGroup || 'GridDD'
58887             });
58888             
58889         }
58890
58891         /*
58892         for(var i = 0; i < colCount; i++){
58893             if(cm.isHidden(i)){
58894                 this.hideColumn(i);
58895             }
58896             if(cm.config[i].align){
58897                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
58898                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
58899             }
58900         }*/
58901         
58902         this.updateHeaderSortState();
58903
58904         this.beforeInitialResize();
58905         this.layout(true);
58906
58907         // two part rendering gives faster view to the user
58908         this.renderPhase2.defer(1, this);
58909     },
58910
58911     renderPhase2 : function(){
58912         // render the rows now
58913         this.refresh();
58914         if(this.grid.autoSizeColumns){
58915             this.autoSizeColumns();
58916         }
58917     },
58918
58919     beforeInitialResize : function(){
58920
58921     },
58922
58923     onColumnSplitterMoved : function(i, w){
58924         this.userResized = true;
58925         var cm = this.grid.colModel;
58926         cm.setColumnWidth(i, w, true);
58927         var cid = cm.getColumnId(i);
58928         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58929         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
58930         this.updateSplitters();
58931         this.layout();
58932         this.grid.fireEvent("columnresize", i, w);
58933     },
58934
58935     syncRowHeights : function(startIndex, endIndex){
58936         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
58937             startIndex = startIndex || 0;
58938             var mrows = this.getBodyTable().rows;
58939             var lrows = this.getLockedTable().rows;
58940             var len = mrows.length-1;
58941             endIndex = Math.min(endIndex || len, len);
58942             for(var i = startIndex; i <= endIndex; i++){
58943                 var m = mrows[i], l = lrows[i];
58944                 var h = Math.max(m.offsetHeight, l.offsetHeight);
58945                 m.style.height = l.style.height = h + "px";
58946             }
58947         }
58948     },
58949
58950     layout : function(initialRender, is2ndPass)
58951     {
58952         var g = this.grid;
58953         var auto = g.autoHeight;
58954         var scrollOffset = 16;
58955         var c = g.getGridEl(), cm = this.cm,
58956                 expandCol = g.autoExpandColumn,
58957                 gv = this;
58958         //c.beginMeasure();
58959
58960         if(!c.dom.offsetWidth){ // display:none?
58961             if(initialRender){
58962                 this.lockedWrap.show();
58963                 this.mainWrap.show();
58964             }
58965             return;
58966         }
58967
58968         var hasLock = this.cm.isLocked(0);
58969
58970         var tbh = this.headerPanel.getHeight();
58971         var bbh = this.footerPanel.getHeight();
58972
58973         if(auto){
58974             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
58975             var newHeight = ch + c.getBorderWidth("tb");
58976             if(g.maxHeight){
58977                 newHeight = Math.min(g.maxHeight, newHeight);
58978             }
58979             c.setHeight(newHeight);
58980         }
58981
58982         if(g.autoWidth){
58983             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
58984         }
58985
58986         var s = this.scroller;
58987
58988         var csize = c.getSize(true);
58989
58990         this.el.setSize(csize.width, csize.height);
58991
58992         this.headerPanel.setWidth(csize.width);
58993         this.footerPanel.setWidth(csize.width);
58994
58995         var hdHeight = this.mainHd.getHeight();
58996         var vw = csize.width;
58997         var vh = csize.height - (tbh + bbh);
58998
58999         s.setSize(vw, vh);
59000
59001         var bt = this.getBodyTable();
59002         
59003         if(cm.getLockedCount() == cm.config.length){
59004             bt = this.getLockedTable();
59005         }
59006         
59007         var ltWidth = hasLock ?
59008                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59009
59010         var scrollHeight = bt.offsetHeight;
59011         var scrollWidth = ltWidth + bt.offsetWidth;
59012         var vscroll = false, hscroll = false;
59013
59014         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59015
59016         var lw = this.lockedWrap, mw = this.mainWrap;
59017         var lb = this.lockedBody, mb = this.mainBody;
59018
59019         setTimeout(function(){
59020             var t = s.dom.offsetTop;
59021             var w = s.dom.clientWidth,
59022                 h = s.dom.clientHeight;
59023
59024             lw.setTop(t);
59025             lw.setSize(ltWidth, h);
59026
59027             mw.setLeftTop(ltWidth, t);
59028             mw.setSize(w-ltWidth, h);
59029
59030             lb.setHeight(h-hdHeight);
59031             mb.setHeight(h-hdHeight);
59032
59033             if(is2ndPass !== true && !gv.userResized && expandCol){
59034                 // high speed resize without full column calculation
59035                 
59036                 var ci = cm.getIndexById(expandCol);
59037                 if (ci < 0) {
59038                     ci = cm.findColumnIndex(expandCol);
59039                 }
59040                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59041                 var expandId = cm.getColumnId(ci);
59042                 var  tw = cm.getTotalWidth(false);
59043                 var currentWidth = cm.getColumnWidth(ci);
59044                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59045                 if(currentWidth != cw){
59046                     cm.setColumnWidth(ci, cw, true);
59047                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59048                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59049                     gv.updateSplitters();
59050                     gv.layout(false, true);
59051                 }
59052             }
59053
59054             if(initialRender){
59055                 lw.show();
59056                 mw.show();
59057             }
59058             //c.endMeasure();
59059         }, 10);
59060     },
59061
59062     onWindowResize : function(){
59063         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59064             return;
59065         }
59066         this.layout();
59067     },
59068
59069     appendFooter : function(parentEl){
59070         return null;
59071     },
59072
59073     sortAscText : "Sort Ascending",
59074     sortDescText : "Sort Descending",
59075     lockText : "Lock Column",
59076     unlockText : "Unlock Column",
59077     columnsText : "Columns",
59078  
59079     columnsWiderText : "Wider",
59080     columnsNarrowText : "Thinner"
59081 });
59082
59083
59084 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59085     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59086     this.proxy.el.addClass('x-grid3-col-dd');
59087 };
59088
59089 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59090     handleMouseDown : function(e){
59091
59092     },
59093
59094     callHandleMouseDown : function(e){
59095         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59096     }
59097 });
59098 /*
59099  * Based on:
59100  * Ext JS Library 1.1.1
59101  * Copyright(c) 2006-2007, Ext JS, LLC.
59102  *
59103  * Originally Released Under LGPL - original licence link has changed is not relivant.
59104  *
59105  * Fork - LGPL
59106  * <script type="text/javascript">
59107  */
59108  /**
59109  * @extends Roo.dd.DDProxy
59110  * @class Roo.grid.SplitDragZone
59111  * Support for Column Header resizing
59112  * @constructor
59113  * @param {Object} config
59114  */
59115 // private
59116 // This is a support class used internally by the Grid components
59117 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59118     this.grid = grid;
59119     this.view = grid.getView();
59120     this.proxy = this.view.resizeProxy;
59121     Roo.grid.SplitDragZone.superclass.constructor.call(
59122         this,
59123         hd, // ID
59124         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59125         {  // CONFIG
59126             dragElId : Roo.id(this.proxy.dom),
59127             resizeFrame:false
59128         }
59129     );
59130     
59131     this.setHandleElId(Roo.id(hd));
59132     if (hd2 !== false) {
59133         this.setOuterHandleElId(Roo.id(hd2));
59134     }
59135     
59136     this.scroll = false;
59137 };
59138 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59139     fly: Roo.Element.fly,
59140
59141     b4StartDrag : function(x, y){
59142         this.view.headersDisabled = true;
59143         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59144                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59145         );
59146         this.proxy.setHeight(h);
59147         
59148         // for old system colWidth really stored the actual width?
59149         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59150         // which in reality did not work.. - it worked only for fixed sizes
59151         // for resizable we need to use actual sizes.
59152         var w = this.cm.getColumnWidth(this.cellIndex);
59153         if (!this.view.mainWrap) {
59154             // bootstrap.
59155             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59156         }
59157         
59158         
59159         
59160         // this was w-this.grid.minColumnWidth;
59161         // doesnt really make sense? - w = thie curren width or the rendered one?
59162         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59163         this.resetConstraints();
59164         this.setXConstraint(minw, 1000);
59165         this.setYConstraint(0, 0);
59166         this.minX = x - minw;
59167         this.maxX = x + 1000;
59168         this.startPos = x;
59169         if (!this.view.mainWrap) { // this is Bootstrap code..
59170             this.getDragEl().style.display='block';
59171         }
59172         
59173         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59174     },
59175
59176
59177     handleMouseDown : function(e){
59178         ev = Roo.EventObject.setEvent(e);
59179         var t = this.fly(ev.getTarget());
59180         if(t.hasClass("x-grid-split")){
59181             this.cellIndex = this.view.getCellIndex(t.dom);
59182             this.split = t.dom;
59183             this.cm = this.grid.colModel;
59184             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59185                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59186             }
59187         }
59188     },
59189
59190     endDrag : function(e){
59191         this.view.headersDisabled = false;
59192         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
59193         var diff = endX - this.startPos;
59194         // 
59195         var w = this.cm.getColumnWidth(this.cellIndex);
59196         if (!this.view.mainWrap) {
59197             w = 0;
59198         }
59199         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
59200     },
59201
59202     autoOffset : function(){
59203         this.setDelta(0,0);
59204     }
59205 });/*
59206  * Based on:
59207  * Ext JS Library 1.1.1
59208  * Copyright(c) 2006-2007, Ext JS, LLC.
59209  *
59210  * Originally Released Under LGPL - original licence link has changed is not relivant.
59211  *
59212  * Fork - LGPL
59213  * <script type="text/javascript">
59214  */
59215  
59216 // private
59217 // This is a support class used internally by the Grid components
59218 Roo.grid.GridDragZone = function(grid, config){
59219     this.view = grid.getView();
59220     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59221     if(this.view.lockedBody){
59222         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59223         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59224     }
59225     this.scroll = false;
59226     this.grid = grid;
59227     this.ddel = document.createElement('div');
59228     this.ddel.className = 'x-grid-dd-wrap';
59229 };
59230
59231 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59232     ddGroup : "GridDD",
59233
59234     getDragData : function(e){
59235         var t = Roo.lib.Event.getTarget(e);
59236         var rowIndex = this.view.findRowIndex(t);
59237         var sm = this.grid.selModel;
59238             
59239         //Roo.log(rowIndex);
59240         
59241         if (sm.getSelectedCell) {
59242             // cell selection..
59243             if (!sm.getSelectedCell()) {
59244                 return false;
59245             }
59246             if (rowIndex != sm.getSelectedCell()[0]) {
59247                 return false;
59248             }
59249         
59250         }
59251         if (sm.getSelections && sm.getSelections().length < 1) {
59252             return false;
59253         }
59254         
59255         
59256         // before it used to all dragging of unseleted... - now we dont do that.
59257         if(rowIndex !== false){
59258             
59259             // if editorgrid.. 
59260             
59261             
59262             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59263                
59264             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59265               //  
59266             //}
59267             if (e.hasModifier()){
59268                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59269             }
59270             
59271             Roo.log("getDragData");
59272             
59273             return {
59274                 grid: this.grid,
59275                 ddel: this.ddel,
59276                 rowIndex: rowIndex,
59277                 selections: sm.getSelections ? sm.getSelections() : (
59278                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59279             };
59280         }
59281         return false;
59282     },
59283     
59284     
59285     onInitDrag : function(e){
59286         var data = this.dragData;
59287         this.ddel.innerHTML = this.grid.getDragDropText();
59288         this.proxy.update(this.ddel);
59289         // fire start drag?
59290     },
59291
59292     afterRepair : function(){
59293         this.dragging = false;
59294     },
59295
59296     getRepairXY : function(e, data){
59297         return false;
59298     },
59299
59300     onEndDrag : function(data, e){
59301         // fire end drag?
59302     },
59303
59304     onValidDrop : function(dd, e, id){
59305         // fire drag drop?
59306         this.hideProxy();
59307     },
59308
59309     beforeInvalidDrop : function(e, id){
59310
59311     }
59312 });/*
59313  * Based on:
59314  * Ext JS Library 1.1.1
59315  * Copyright(c) 2006-2007, Ext JS, LLC.
59316  *
59317  * Originally Released Under LGPL - original licence link has changed is not relivant.
59318  *
59319  * Fork - LGPL
59320  * <script type="text/javascript">
59321  */
59322  
59323
59324 /**
59325  * @class Roo.grid.ColumnModel
59326  * @extends Roo.util.Observable
59327  * This is the default implementation of a ColumnModel used by the Grid. It defines
59328  * the columns in the grid.
59329  * <br>Usage:<br>
59330  <pre><code>
59331  var colModel = new Roo.grid.ColumnModel([
59332         {header: "Ticker", width: 60, sortable: true, locked: true},
59333         {header: "Company Name", width: 150, sortable: true},
59334         {header: "Market Cap.", width: 100, sortable: true},
59335         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59336         {header: "Employees", width: 100, sortable: true, resizable: false}
59337  ]);
59338  </code></pre>
59339  * <p>
59340  
59341  * The config options listed for this class are options which may appear in each
59342  * individual column definition.
59343  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59344  * @constructor
59345  * @param {Object} config An Array of column config objects. See this class's
59346  * config objects for details.
59347 */
59348 Roo.grid.ColumnModel = function(config){
59349         /**
59350      * The config passed into the constructor
59351      */
59352     this.config = []; //config;
59353     this.lookup = {};
59354
59355     // if no id, create one
59356     // if the column does not have a dataIndex mapping,
59357     // map it to the order it is in the config
59358     for(var i = 0, len = config.length; i < len; i++){
59359         this.addColumn(config[i]);
59360         
59361     }
59362
59363     /**
59364      * The width of columns which have no width specified (defaults to 100)
59365      * @type Number
59366      */
59367     this.defaultWidth = 100;
59368
59369     /**
59370      * Default sortable of columns which have no sortable specified (defaults to false)
59371      * @type Boolean
59372      */
59373     this.defaultSortable = false;
59374
59375     this.addEvents({
59376         /**
59377              * @event widthchange
59378              * Fires when the width of a column changes.
59379              * @param {ColumnModel} this
59380              * @param {Number} columnIndex The column index
59381              * @param {Number} newWidth The new width
59382              */
59383             "widthchange": true,
59384         /**
59385              * @event headerchange
59386              * Fires when the text of a header changes.
59387              * @param {ColumnModel} this
59388              * @param {Number} columnIndex The column index
59389              * @param {Number} newText The new header text
59390              */
59391             "headerchange": true,
59392         /**
59393              * @event hiddenchange
59394              * Fires when a column is hidden or "unhidden".
59395              * @param {ColumnModel} this
59396              * @param {Number} columnIndex The column index
59397              * @param {Boolean} hidden true if hidden, false otherwise
59398              */
59399             "hiddenchange": true,
59400             /**
59401          * @event columnmoved
59402          * Fires when a column is moved.
59403          * @param {ColumnModel} this
59404          * @param {Number} oldIndex
59405          * @param {Number} newIndex
59406          */
59407         "columnmoved" : true,
59408         /**
59409          * @event columlockchange
59410          * Fires when a column's locked state is changed
59411          * @param {ColumnModel} this
59412          * @param {Number} colIndex
59413          * @param {Boolean} locked true if locked
59414          */
59415         "columnlockchange" : true
59416     });
59417     Roo.grid.ColumnModel.superclass.constructor.call(this);
59418 };
59419 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59420     /**
59421      * @cfg {String} header The header text to display in the Grid view.
59422      */
59423         /**
59424      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59425      */
59426         /**
59427      * @cfg {String} smHeader Header at Bootsrap Small width
59428      */
59429         /**
59430      * @cfg {String} mdHeader Header at Bootsrap Medium width
59431      */
59432         /**
59433      * @cfg {String} lgHeader Header at Bootsrap Large width
59434      */
59435         /**
59436      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59437      */
59438     /**
59439      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59440      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59441      * specified, the column's index is used as an index into the Record's data Array.
59442      */
59443     /**
59444      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59445      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59446      */
59447     /**
59448      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59449      * Defaults to the value of the {@link #defaultSortable} property.
59450      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59451      */
59452     /**
59453      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59454      */
59455     /**
59456      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59457      */
59458     /**
59459      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59460      */
59461     /**
59462      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59463      */
59464     /**
59465      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59466      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59467      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59468      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59469      */
59470        /**
59471      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59472      */
59473     /**
59474      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59475      */
59476     /**
59477      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59478      */
59479     /**
59480      * @cfg {String} cursor (Optional)
59481      */
59482     /**
59483      * @cfg {String} tooltip (Optional)
59484      */
59485     /**
59486      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59487      */
59488     /**
59489      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59490      */
59491     /**
59492      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59493      */
59494     /**
59495      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59496      */
59497         /**
59498      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59499      */
59500     /**
59501      * Returns the id of the column at the specified index.
59502      * @param {Number} index The column index
59503      * @return {String} the id
59504      */
59505     getColumnId : function(index){
59506         return this.config[index].id;
59507     },
59508
59509     /**
59510      * Returns the column for a specified id.
59511      * @param {String} id The column id
59512      * @return {Object} the column
59513      */
59514     getColumnById : function(id){
59515         return this.lookup[id];
59516     },
59517
59518     
59519     /**
59520      * Returns the column Object for a specified dataIndex.
59521      * @param {String} dataIndex The column dataIndex
59522      * @return {Object|Boolean} the column or false if not found
59523      */
59524     getColumnByDataIndex: function(dataIndex){
59525         var index = this.findColumnIndex(dataIndex);
59526         return index > -1 ? this.config[index] : false;
59527     },
59528     
59529     /**
59530      * Returns the index for a specified column id.
59531      * @param {String} id The column id
59532      * @return {Number} the index, or -1 if not found
59533      */
59534     getIndexById : function(id){
59535         for(var i = 0, len = this.config.length; i < len; i++){
59536             if(this.config[i].id == id){
59537                 return i;
59538             }
59539         }
59540         return -1;
59541     },
59542     
59543     /**
59544      * Returns the index for a specified column dataIndex.
59545      * @param {String} dataIndex The column dataIndex
59546      * @return {Number} the index, or -1 if not found
59547      */
59548     
59549     findColumnIndex : function(dataIndex){
59550         for(var i = 0, len = this.config.length; i < len; i++){
59551             if(this.config[i].dataIndex == dataIndex){
59552                 return i;
59553             }
59554         }
59555         return -1;
59556     },
59557     
59558     
59559     moveColumn : function(oldIndex, newIndex){
59560         var c = this.config[oldIndex];
59561         this.config.splice(oldIndex, 1);
59562         this.config.splice(newIndex, 0, c);
59563         this.dataMap = null;
59564         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59565     },
59566
59567     isLocked : function(colIndex){
59568         return this.config[colIndex].locked === true;
59569     },
59570
59571     setLocked : function(colIndex, value, suppressEvent){
59572         if(this.isLocked(colIndex) == value){
59573             return;
59574         }
59575         this.config[colIndex].locked = value;
59576         if(!suppressEvent){
59577             this.fireEvent("columnlockchange", this, colIndex, value);
59578         }
59579     },
59580
59581     getTotalLockedWidth : function(){
59582         var totalWidth = 0;
59583         for(var i = 0; i < this.config.length; i++){
59584             if(this.isLocked(i) && !this.isHidden(i)){
59585                 this.totalWidth += this.getColumnWidth(i);
59586             }
59587         }
59588         return totalWidth;
59589     },
59590
59591     getLockedCount : function(){
59592         for(var i = 0, len = this.config.length; i < len; i++){
59593             if(!this.isLocked(i)){
59594                 return i;
59595             }
59596         }
59597         
59598         return this.config.length;
59599     },
59600
59601     /**
59602      * Returns the number of columns.
59603      * @return {Number}
59604      */
59605     getColumnCount : function(visibleOnly){
59606         if(visibleOnly === true){
59607             var c = 0;
59608             for(var i = 0, len = this.config.length; i < len; i++){
59609                 if(!this.isHidden(i)){
59610                     c++;
59611                 }
59612             }
59613             return c;
59614         }
59615         return this.config.length;
59616     },
59617
59618     /**
59619      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59620      * @param {Function} fn
59621      * @param {Object} scope (optional)
59622      * @return {Array} result
59623      */
59624     getColumnsBy : function(fn, scope){
59625         var r = [];
59626         for(var i = 0, len = this.config.length; i < len; i++){
59627             var c = this.config[i];
59628             if(fn.call(scope||this, c, i) === true){
59629                 r[r.length] = c;
59630             }
59631         }
59632         return r;
59633     },
59634
59635     /**
59636      * Returns true if the specified column is sortable.
59637      * @param {Number} col The column index
59638      * @return {Boolean}
59639      */
59640     isSortable : function(col){
59641         if(typeof this.config[col].sortable == "undefined"){
59642             return this.defaultSortable;
59643         }
59644         return this.config[col].sortable;
59645     },
59646
59647     /**
59648      * Returns the rendering (formatting) function defined for the column.
59649      * @param {Number} col The column index.
59650      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
59651      */
59652     getRenderer : function(col){
59653         if(!this.config[col].renderer){
59654             return Roo.grid.ColumnModel.defaultRenderer;
59655         }
59656         return this.config[col].renderer;
59657     },
59658
59659     /**
59660      * Sets the rendering (formatting) function for a column.
59661      * @param {Number} col The column index
59662      * @param {Function} fn The function to use to process the cell's raw data
59663      * to return HTML markup for the grid view. The render function is called with
59664      * the following parameters:<ul>
59665      * <li>Data value.</li>
59666      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
59667      * <li>css A CSS style string to apply to the table cell.</li>
59668      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
59669      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
59670      * <li>Row index</li>
59671      * <li>Column index</li>
59672      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
59673      */
59674     setRenderer : function(col, fn){
59675         this.config[col].renderer = fn;
59676     },
59677
59678     /**
59679      * Returns the width for the specified column.
59680      * @param {Number} col The column index
59681      * @param (optional) {String} gridSize bootstrap width size.
59682      * @return {Number}
59683      */
59684     getColumnWidth : function(col, gridSize)
59685         {
59686                 var cfg = this.config[col];
59687                 
59688                 if (typeof(gridSize) == 'undefined') {
59689                         return cfg.width * 1 || this.defaultWidth;
59690                 }
59691                 if (gridSize === false) { // if we set it..
59692                         return cfg.width || false;
59693                 }
59694                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
59695                 
59696                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
59697                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
59698                                 continue;
59699                         }
59700                         return cfg[ sizes[i] ];
59701                 }
59702                 return 1;
59703                 
59704     },
59705
59706     /**
59707      * Sets the width for a column.
59708      * @param {Number} col The column index
59709      * @param {Number} width The new width
59710      */
59711     setColumnWidth : function(col, width, suppressEvent){
59712         this.config[col].width = width;
59713         this.totalWidth = null;
59714         if(!suppressEvent){
59715              this.fireEvent("widthchange", this, col, width);
59716         }
59717     },
59718
59719     /**
59720      * Returns the total width of all columns.
59721      * @param {Boolean} includeHidden True to include hidden column widths
59722      * @return {Number}
59723      */
59724     getTotalWidth : function(includeHidden){
59725         if(!this.totalWidth){
59726             this.totalWidth = 0;
59727             for(var i = 0, len = this.config.length; i < len; i++){
59728                 if(includeHidden || !this.isHidden(i)){
59729                     this.totalWidth += this.getColumnWidth(i);
59730                 }
59731             }
59732         }
59733         return this.totalWidth;
59734     },
59735
59736     /**
59737      * Returns the header for the specified column.
59738      * @param {Number} col The column index
59739      * @return {String}
59740      */
59741     getColumnHeader : function(col){
59742         return this.config[col].header;
59743     },
59744
59745     /**
59746      * Sets the header for a column.
59747      * @param {Number} col The column index
59748      * @param {String} header The new header
59749      */
59750     setColumnHeader : function(col, header){
59751         this.config[col].header = header;
59752         this.fireEvent("headerchange", this, col, header);
59753     },
59754
59755     /**
59756      * Returns the tooltip for the specified column.
59757      * @param {Number} col The column index
59758      * @return {String}
59759      */
59760     getColumnTooltip : function(col){
59761             return this.config[col].tooltip;
59762     },
59763     /**
59764      * Sets the tooltip for a column.
59765      * @param {Number} col The column index
59766      * @param {String} tooltip The new tooltip
59767      */
59768     setColumnTooltip : function(col, tooltip){
59769             this.config[col].tooltip = tooltip;
59770     },
59771
59772     /**
59773      * Returns the dataIndex for the specified column.
59774      * @param {Number} col The column index
59775      * @return {Number}
59776      */
59777     getDataIndex : function(col){
59778         return this.config[col].dataIndex;
59779     },
59780
59781     /**
59782      * Sets the dataIndex for a column.
59783      * @param {Number} col The column index
59784      * @param {Number} dataIndex The new dataIndex
59785      */
59786     setDataIndex : function(col, dataIndex){
59787         this.config[col].dataIndex = dataIndex;
59788     },
59789
59790     
59791     
59792     /**
59793      * Returns true if the cell is editable.
59794      * @param {Number} colIndex The column index
59795      * @param {Number} rowIndex The row index - this is nto actually used..?
59796      * @return {Boolean}
59797      */
59798     isCellEditable : function(colIndex, rowIndex){
59799         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
59800     },
59801
59802     /**
59803      * Returns the editor defined for the cell/column.
59804      * return false or null to disable editing.
59805      * @param {Number} colIndex The column index
59806      * @param {Number} rowIndex The row index
59807      * @return {Object}
59808      */
59809     getCellEditor : function(colIndex, rowIndex){
59810         return this.config[colIndex].editor;
59811     },
59812
59813     /**
59814      * Sets if a column is editable.
59815      * @param {Number} col The column index
59816      * @param {Boolean} editable True if the column is editable
59817      */
59818     setEditable : function(col, editable){
59819         this.config[col].editable = editable;
59820     },
59821
59822
59823     /**
59824      * Returns true if the column is hidden.
59825      * @param {Number} colIndex The column index
59826      * @return {Boolean}
59827      */
59828     isHidden : function(colIndex){
59829         return this.config[colIndex].hidden;
59830     },
59831
59832
59833     /**
59834      * Returns true if the column width cannot be changed
59835      */
59836     isFixed : function(colIndex){
59837         return this.config[colIndex].fixed;
59838     },
59839
59840     /**
59841      * Returns true if the column can be resized
59842      * @return {Boolean}
59843      */
59844     isResizable : function(colIndex){
59845         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
59846     },
59847     /**
59848      * Sets if a column is hidden.
59849      * @param {Number} colIndex The column index
59850      * @param {Boolean} hidden True if the column is hidden
59851      */
59852     setHidden : function(colIndex, hidden){
59853         this.config[colIndex].hidden = hidden;
59854         this.totalWidth = null;
59855         this.fireEvent("hiddenchange", this, colIndex, hidden);
59856     },
59857
59858     /**
59859      * Sets the editor for a column.
59860      * @param {Number} col The column index
59861      * @param {Object} editor The editor object
59862      */
59863     setEditor : function(col, editor){
59864         this.config[col].editor = editor;
59865     },
59866     /**
59867      * Add a column (experimental...) - defaults to adding to the end..
59868      * @param {Object} config 
59869     */
59870     addColumn : function(c)
59871     {
59872     
59873         var i = this.config.length;
59874         this.config[i] = c;
59875         
59876         if(typeof c.dataIndex == "undefined"){
59877             c.dataIndex = i;
59878         }
59879         if(typeof c.renderer == "string"){
59880             c.renderer = Roo.util.Format[c.renderer];
59881         }
59882         if(typeof c.id == "undefined"){
59883             c.id = Roo.id();
59884         }
59885         if(c.editor && c.editor.xtype){
59886             c.editor  = Roo.factory(c.editor, Roo.grid);
59887         }
59888         if(c.editor && c.editor.isFormField){
59889             c.editor = new Roo.grid.GridEditor(c.editor);
59890         }
59891         this.lookup[c.id] = c;
59892     }
59893     
59894 });
59895
59896 Roo.grid.ColumnModel.defaultRenderer = function(value)
59897 {
59898     if(typeof value == "object") {
59899         return value;
59900     }
59901         if(typeof value == "string" && value.length < 1){
59902             return "&#160;";
59903         }
59904     
59905         return String.format("{0}", value);
59906 };
59907
59908 // Alias for backwards compatibility
59909 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
59910 /*
59911  * Based on:
59912  * Ext JS Library 1.1.1
59913  * Copyright(c) 2006-2007, Ext JS, LLC.
59914  *
59915  * Originally Released Under LGPL - original licence link has changed is not relivant.
59916  *
59917  * Fork - LGPL
59918  * <script type="text/javascript">
59919  */
59920
59921 /**
59922  * @class Roo.grid.AbstractSelectionModel
59923  * @extends Roo.util.Observable
59924  * @abstract
59925  * Abstract base class for grid SelectionModels.  It provides the interface that should be
59926  * implemented by descendant classes.  This class should not be directly instantiated.
59927  * @constructor
59928  */
59929 Roo.grid.AbstractSelectionModel = function(){
59930     this.locked = false;
59931     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
59932 };
59933
59934 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
59935     /** @ignore Called by the grid automatically. Do not call directly. */
59936     init : function(grid){
59937         this.grid = grid;
59938         this.initEvents();
59939     },
59940
59941     /**
59942      * Locks the selections.
59943      */
59944     lock : function(){
59945         this.locked = true;
59946     },
59947
59948     /**
59949      * Unlocks the selections.
59950      */
59951     unlock : function(){
59952         this.locked = false;
59953     },
59954
59955     /**
59956      * Returns true if the selections are locked.
59957      * @return {Boolean}
59958      */
59959     isLocked : function(){
59960         return this.locked;
59961     }
59962 });/*
59963  * Based on:
59964  * Ext JS Library 1.1.1
59965  * Copyright(c) 2006-2007, Ext JS, LLC.
59966  *
59967  * Originally Released Under LGPL - original licence link has changed is not relivant.
59968  *
59969  * Fork - LGPL
59970  * <script type="text/javascript">
59971  */
59972 /**
59973  * @extends Roo.grid.AbstractSelectionModel
59974  * @class Roo.grid.RowSelectionModel
59975  * The default SelectionModel used by {@link Roo.grid.Grid}.
59976  * It supports multiple selections and keyboard selection/navigation. 
59977  * @constructor
59978  * @param {Object} config
59979  */
59980 Roo.grid.RowSelectionModel = function(config){
59981     Roo.apply(this, config);
59982     this.selections = new Roo.util.MixedCollection(false, function(o){
59983         return o.id;
59984     });
59985
59986     this.last = false;
59987     this.lastActive = false;
59988
59989     this.addEvents({
59990         /**
59991         * @event selectionchange
59992         * Fires when the selection changes
59993         * @param {SelectionModel} this
59994         */
59995        "selectionchange" : true,
59996        /**
59997         * @event afterselectionchange
59998         * Fires after the selection changes (eg. by key press or clicking)
59999         * @param {SelectionModel} this
60000         */
60001        "afterselectionchange" : true,
60002        /**
60003         * @event beforerowselect
60004         * Fires when a row is selected being selected, return false to cancel.
60005         * @param {SelectionModel} this
60006         * @param {Number} rowIndex The selected index
60007         * @param {Boolean} keepExisting False if other selections will be cleared
60008         */
60009        "beforerowselect" : true,
60010        /**
60011         * @event rowselect
60012         * Fires when a row is selected.
60013         * @param {SelectionModel} this
60014         * @param {Number} rowIndex The selected index
60015         * @param {Roo.data.Record} r The record
60016         */
60017        "rowselect" : true,
60018        /**
60019         * @event rowdeselect
60020         * Fires when a row is deselected.
60021         * @param {SelectionModel} this
60022         * @param {Number} rowIndex The selected index
60023         */
60024         "rowdeselect" : true
60025     });
60026     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60027     this.locked = false;
60028 };
60029
60030 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60031     /**
60032      * @cfg {Boolean} singleSelect
60033      * True to allow selection of only one row at a time (defaults to false)
60034      */
60035     singleSelect : false,
60036
60037     // private
60038     initEvents : function(){
60039
60040         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60041             this.grid.on("mousedown", this.handleMouseDown, this);
60042         }else{ // allow click to work like normal
60043             this.grid.on("rowclick", this.handleDragableRowClick, this);
60044         }
60045         // bootstrap does not have a view..
60046         var view = this.grid.view ? this.grid.view : this.grid;
60047         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60048             "up" : function(e){
60049                 if(!e.shiftKey){
60050                     this.selectPrevious(e.shiftKey);
60051                 }else if(this.last !== false && this.lastActive !== false){
60052                     var last = this.last;
60053                     this.selectRange(this.last,  this.lastActive-1);
60054                     view.focusRow(this.lastActive);
60055                     if(last !== false){
60056                         this.last = last;
60057                     }
60058                 }else{
60059                     this.selectFirstRow();
60060                 }
60061                 this.fireEvent("afterselectionchange", this);
60062             },
60063             "down" : function(e){
60064                 if(!e.shiftKey){
60065                     this.selectNext(e.shiftKey);
60066                 }else if(this.last !== false && this.lastActive !== false){
60067                     var last = this.last;
60068                     this.selectRange(this.last,  this.lastActive+1);
60069                     view.focusRow(this.lastActive);
60070                     if(last !== false){
60071                         this.last = last;
60072                     }
60073                 }else{
60074                     this.selectFirstRow();
60075                 }
60076                 this.fireEvent("afterselectionchange", this);
60077             },
60078             scope: this
60079         });
60080
60081          
60082         view.on("refresh", this.onRefresh, this);
60083         view.on("rowupdated", this.onRowUpdated, this);
60084         view.on("rowremoved", this.onRemove, this);
60085     },
60086
60087     // private
60088     onRefresh : function(){
60089         var ds = this.grid.ds, i, v = this.grid.view;
60090         var s = this.selections;
60091         s.each(function(r){
60092             if((i = ds.indexOfId(r.id)) != -1){
60093                 v.onRowSelect(i);
60094                 s.add(ds.getAt(i)); // updating the selection relate data
60095             }else{
60096                 s.remove(r);
60097             }
60098         });
60099     },
60100
60101     // private
60102     onRemove : function(v, index, r){
60103         this.selections.remove(r);
60104     },
60105
60106     // private
60107     onRowUpdated : function(v, index, r){
60108         if(this.isSelected(r)){
60109             v.onRowSelect(index);
60110         }
60111     },
60112
60113     /**
60114      * Select records.
60115      * @param {Array} records The records to select
60116      * @param {Boolean} keepExisting (optional) True to keep existing selections
60117      */
60118     selectRecords : function(records, keepExisting){
60119         if(!keepExisting){
60120             this.clearSelections();
60121         }
60122         var ds = this.grid.ds;
60123         for(var i = 0, len = records.length; i < len; i++){
60124             this.selectRow(ds.indexOf(records[i]), true);
60125         }
60126     },
60127
60128     /**
60129      * Gets the number of selected rows.
60130      * @return {Number}
60131      */
60132     getCount : function(){
60133         return this.selections.length;
60134     },
60135
60136     /**
60137      * Selects the first row in the grid.
60138      */
60139     selectFirstRow : function(){
60140         this.selectRow(0);
60141     },
60142
60143     /**
60144      * Select the last row.
60145      * @param {Boolean} keepExisting (optional) True to keep existing selections
60146      */
60147     selectLastRow : function(keepExisting){
60148         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60149     },
60150
60151     /**
60152      * Selects the row immediately following the last selected row.
60153      * @param {Boolean} keepExisting (optional) True to keep existing selections
60154      */
60155     selectNext : function(keepExisting){
60156         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60157             this.selectRow(this.last+1, keepExisting);
60158             var view = this.grid.view ? this.grid.view : this.grid;
60159             view.focusRow(this.last);
60160         }
60161     },
60162
60163     /**
60164      * Selects the row that precedes the last selected row.
60165      * @param {Boolean} keepExisting (optional) True to keep existing selections
60166      */
60167     selectPrevious : function(keepExisting){
60168         if(this.last){
60169             this.selectRow(this.last-1, keepExisting);
60170             var view = this.grid.view ? this.grid.view : this.grid;
60171             view.focusRow(this.last);
60172         }
60173     },
60174
60175     /**
60176      * Returns the selected records
60177      * @return {Array} Array of selected records
60178      */
60179     getSelections : function(){
60180         return [].concat(this.selections.items);
60181     },
60182
60183     /**
60184      * Returns the first selected record.
60185      * @return {Record}
60186      */
60187     getSelected : function(){
60188         return this.selections.itemAt(0);
60189     },
60190
60191
60192     /**
60193      * Clears all selections.
60194      */
60195     clearSelections : function(fast){
60196         if(this.locked) {
60197             return;
60198         }
60199         if(fast !== true){
60200             var ds = this.grid.ds;
60201             var s = this.selections;
60202             s.each(function(r){
60203                 this.deselectRow(ds.indexOfId(r.id));
60204             }, this);
60205             s.clear();
60206         }else{
60207             this.selections.clear();
60208         }
60209         this.last = false;
60210     },
60211
60212
60213     /**
60214      * Selects all rows.
60215      */
60216     selectAll : function(){
60217         if(this.locked) {
60218             return;
60219         }
60220         this.selections.clear();
60221         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60222             this.selectRow(i, true);
60223         }
60224     },
60225
60226     /**
60227      * Returns True if there is a selection.
60228      * @return {Boolean}
60229      */
60230     hasSelection : function(){
60231         return this.selections.length > 0;
60232     },
60233
60234     /**
60235      * Returns True if the specified row is selected.
60236      * @param {Number/Record} record The record or index of the record to check
60237      * @return {Boolean}
60238      */
60239     isSelected : function(index){
60240         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60241         return (r && this.selections.key(r.id) ? true : false);
60242     },
60243
60244     /**
60245      * Returns True if the specified record id is selected.
60246      * @param {String} id The id of record to check
60247      * @return {Boolean}
60248      */
60249     isIdSelected : function(id){
60250         return (this.selections.key(id) ? true : false);
60251     },
60252
60253     // private
60254     handleMouseDown : function(e, t)
60255     {
60256         var view = this.grid.view ? this.grid.view : this.grid;
60257         var rowIndex;
60258         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60259             return;
60260         };
60261         if(e.shiftKey && this.last !== false){
60262             var last = this.last;
60263             this.selectRange(last, rowIndex, e.ctrlKey);
60264             this.last = last; // reset the last
60265             view.focusRow(rowIndex);
60266         }else{
60267             var isSelected = this.isSelected(rowIndex);
60268             if(e.button !== 0 && isSelected){
60269                 view.focusRow(rowIndex);
60270             }else if(e.ctrlKey && isSelected){
60271                 this.deselectRow(rowIndex);
60272             }else if(!isSelected){
60273                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60274                 view.focusRow(rowIndex);
60275             }
60276         }
60277         this.fireEvent("afterselectionchange", this);
60278     },
60279     // private
60280     handleDragableRowClick :  function(grid, rowIndex, e) 
60281     {
60282         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60283             this.selectRow(rowIndex, false);
60284             var view = this.grid.view ? this.grid.view : this.grid;
60285             view.focusRow(rowIndex);
60286              this.fireEvent("afterselectionchange", this);
60287         }
60288     },
60289     
60290     /**
60291      * Selects multiple rows.
60292      * @param {Array} rows Array of the indexes of the row to select
60293      * @param {Boolean} keepExisting (optional) True to keep existing selections
60294      */
60295     selectRows : function(rows, keepExisting){
60296         if(!keepExisting){
60297             this.clearSelections();
60298         }
60299         for(var i = 0, len = rows.length; i < len; i++){
60300             this.selectRow(rows[i], true);
60301         }
60302     },
60303
60304     /**
60305      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60306      * @param {Number} startRow The index of the first row in the range
60307      * @param {Number} endRow The index of the last row in the range
60308      * @param {Boolean} keepExisting (optional) True to retain existing selections
60309      */
60310     selectRange : function(startRow, endRow, keepExisting){
60311         if(this.locked) {
60312             return;
60313         }
60314         if(!keepExisting){
60315             this.clearSelections();
60316         }
60317         if(startRow <= endRow){
60318             for(var i = startRow; i <= endRow; i++){
60319                 this.selectRow(i, true);
60320             }
60321         }else{
60322             for(var i = startRow; i >= endRow; i--){
60323                 this.selectRow(i, true);
60324             }
60325         }
60326     },
60327
60328     /**
60329      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60330      * @param {Number} startRow The index of the first row in the range
60331      * @param {Number} endRow The index of the last row in the range
60332      */
60333     deselectRange : function(startRow, endRow, preventViewNotify){
60334         if(this.locked) {
60335             return;
60336         }
60337         for(var i = startRow; i <= endRow; i++){
60338             this.deselectRow(i, preventViewNotify);
60339         }
60340     },
60341
60342     /**
60343      * Selects a row.
60344      * @param {Number} row The index of the row to select
60345      * @param {Boolean} keepExisting (optional) True to keep existing selections
60346      */
60347     selectRow : function(index, keepExisting, preventViewNotify){
60348         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60349             return;
60350         }
60351         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60352             if(!keepExisting || this.singleSelect){
60353                 this.clearSelections();
60354             }
60355             var r = this.grid.ds.getAt(index);
60356             this.selections.add(r);
60357             this.last = this.lastActive = index;
60358             if(!preventViewNotify){
60359                 var view = this.grid.view ? this.grid.view : this.grid;
60360                 view.onRowSelect(index);
60361             }
60362             this.fireEvent("rowselect", this, index, r);
60363             this.fireEvent("selectionchange", this);
60364         }
60365     },
60366
60367     /**
60368      * Deselects a row.
60369      * @param {Number} row The index of the row to deselect
60370      */
60371     deselectRow : function(index, preventViewNotify){
60372         if(this.locked) {
60373             return;
60374         }
60375         if(this.last == index){
60376             this.last = false;
60377         }
60378         if(this.lastActive == index){
60379             this.lastActive = false;
60380         }
60381         var r = this.grid.ds.getAt(index);
60382         this.selections.remove(r);
60383         if(!preventViewNotify){
60384             var view = this.grid.view ? this.grid.view : this.grid;
60385             view.onRowDeselect(index);
60386         }
60387         this.fireEvent("rowdeselect", this, index);
60388         this.fireEvent("selectionchange", this);
60389     },
60390
60391     // private
60392     restoreLast : function(){
60393         if(this._last){
60394             this.last = this._last;
60395         }
60396     },
60397
60398     // private
60399     acceptsNav : function(row, col, cm){
60400         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60401     },
60402
60403     // private
60404     onEditorKey : function(field, e){
60405         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60406         if(k == e.TAB){
60407             e.stopEvent();
60408             ed.completeEdit();
60409             if(e.shiftKey){
60410                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60411             }else{
60412                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60413             }
60414         }else if(k == e.ENTER && !e.ctrlKey){
60415             e.stopEvent();
60416             ed.completeEdit();
60417             if(e.shiftKey){
60418                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60419             }else{
60420                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60421             }
60422         }else if(k == e.ESC){
60423             ed.cancelEdit();
60424         }
60425         if(newCell){
60426             g.startEditing(newCell[0], newCell[1]);
60427         }
60428     }
60429 });/*
60430  * Based on:
60431  * Ext JS Library 1.1.1
60432  * Copyright(c) 2006-2007, Ext JS, LLC.
60433  *
60434  * Originally Released Under LGPL - original licence link has changed is not relivant.
60435  *
60436  * Fork - LGPL
60437  * <script type="text/javascript">
60438  */
60439 /**
60440  * @class Roo.grid.CellSelectionModel
60441  * @extends Roo.grid.AbstractSelectionModel
60442  * This class provides the basic implementation for cell selection in a grid.
60443  * @constructor
60444  * @param {Object} config The object containing the configuration of this model.
60445  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60446  */
60447 Roo.grid.CellSelectionModel = function(config){
60448     Roo.apply(this, config);
60449
60450     this.selection = null;
60451
60452     this.addEvents({
60453         /**
60454              * @event beforerowselect
60455              * Fires before a cell is selected.
60456              * @param {SelectionModel} this
60457              * @param {Number} rowIndex The selected row index
60458              * @param {Number} colIndex The selected cell index
60459              */
60460             "beforecellselect" : true,
60461         /**
60462              * @event cellselect
60463              * Fires when a cell is selected.
60464              * @param {SelectionModel} this
60465              * @param {Number} rowIndex The selected row index
60466              * @param {Number} colIndex The selected cell index
60467              */
60468             "cellselect" : true,
60469         /**
60470              * @event selectionchange
60471              * Fires when the active selection changes.
60472              * @param {SelectionModel} this
60473              * @param {Object} selection null for no selection or an object (o) with two properties
60474                 <ul>
60475                 <li>o.record: the record object for the row the selection is in</li>
60476                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60477                 </ul>
60478              */
60479             "selectionchange" : true,
60480         /**
60481              * @event tabend
60482              * Fires when the tab (or enter) was pressed on the last editable cell
60483              * You can use this to trigger add new row.
60484              * @param {SelectionModel} this
60485              */
60486             "tabend" : true,
60487          /**
60488              * @event beforeeditnext
60489              * Fires before the next editable sell is made active
60490              * You can use this to skip to another cell or fire the tabend
60491              *    if you set cell to false
60492              * @param {Object} eventdata object : { cell : [ row, col ] } 
60493              */
60494             "beforeeditnext" : true
60495     });
60496     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60497 };
60498
60499 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60500     
60501     enter_is_tab: false,
60502
60503     /** @ignore */
60504     initEvents : function(){
60505         this.grid.on("mousedown", this.handleMouseDown, this);
60506         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60507         var view = this.grid.view;
60508         view.on("refresh", this.onViewChange, this);
60509         view.on("rowupdated", this.onRowUpdated, this);
60510         view.on("beforerowremoved", this.clearSelections, this);
60511         view.on("beforerowsinserted", this.clearSelections, this);
60512         if(this.grid.isEditor){
60513             this.grid.on("beforeedit", this.beforeEdit,  this);
60514         }
60515     },
60516
60517         //private
60518     beforeEdit : function(e){
60519         this.select(e.row, e.column, false, true, e.record);
60520     },
60521
60522         //private
60523     onRowUpdated : function(v, index, r){
60524         if(this.selection && this.selection.record == r){
60525             v.onCellSelect(index, this.selection.cell[1]);
60526         }
60527     },
60528
60529         //private
60530     onViewChange : function(){
60531         this.clearSelections(true);
60532     },
60533
60534         /**
60535          * Returns the currently selected cell,.
60536          * @return {Array} The selected cell (row, column) or null if none selected.
60537          */
60538     getSelectedCell : function(){
60539         return this.selection ? this.selection.cell : null;
60540     },
60541
60542     /**
60543      * Clears all selections.
60544      * @param {Boolean} true to prevent the gridview from being notified about the change.
60545      */
60546     clearSelections : function(preventNotify){
60547         var s = this.selection;
60548         if(s){
60549             if(preventNotify !== true){
60550                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60551             }
60552             this.selection = null;
60553             this.fireEvent("selectionchange", this, null);
60554         }
60555     },
60556
60557     /**
60558      * Returns true if there is a selection.
60559      * @return {Boolean}
60560      */
60561     hasSelection : function(){
60562         return this.selection ? true : false;
60563     },
60564
60565     /** @ignore */
60566     handleMouseDown : function(e, t){
60567         var v = this.grid.getView();
60568         if(this.isLocked()){
60569             return;
60570         };
60571         var row = v.findRowIndex(t);
60572         var cell = v.findCellIndex(t);
60573         if(row !== false && cell !== false){
60574             this.select(row, cell);
60575         }
60576     },
60577
60578     /**
60579      * Selects a cell.
60580      * @param {Number} rowIndex
60581      * @param {Number} collIndex
60582      */
60583     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60584         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60585             this.clearSelections();
60586             r = r || this.grid.dataSource.getAt(rowIndex);
60587             this.selection = {
60588                 record : r,
60589                 cell : [rowIndex, colIndex]
60590             };
60591             if(!preventViewNotify){
60592                 var v = this.grid.getView();
60593                 v.onCellSelect(rowIndex, colIndex);
60594                 if(preventFocus !== true){
60595                     v.focusCell(rowIndex, colIndex);
60596                 }
60597             }
60598             this.fireEvent("cellselect", this, rowIndex, colIndex);
60599             this.fireEvent("selectionchange", this, this.selection);
60600         }
60601     },
60602
60603         //private
60604     isSelectable : function(rowIndex, colIndex, cm){
60605         return !cm.isHidden(colIndex);
60606     },
60607
60608     /** @ignore */
60609     handleKeyDown : function(e){
60610         //Roo.log('Cell Sel Model handleKeyDown');
60611         if(!e.isNavKeyPress()){
60612             return;
60613         }
60614         var g = this.grid, s = this.selection;
60615         if(!s){
60616             e.stopEvent();
60617             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60618             if(cell){
60619                 this.select(cell[0], cell[1]);
60620             }
60621             return;
60622         }
60623         var sm = this;
60624         var walk = function(row, col, step){
60625             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60626         };
60627         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60628         var newCell;
60629
60630       
60631
60632         switch(k){
60633             case e.TAB:
60634                 // handled by onEditorKey
60635                 if (g.isEditor && g.editing) {
60636                     return;
60637                 }
60638                 if(e.shiftKey) {
60639                     newCell = walk(r, c-1, -1);
60640                 } else {
60641                     newCell = walk(r, c+1, 1);
60642                 }
60643                 break;
60644             
60645             case e.DOWN:
60646                newCell = walk(r+1, c, 1);
60647                 break;
60648             
60649             case e.UP:
60650                 newCell = walk(r-1, c, -1);
60651                 break;
60652             
60653             case e.RIGHT:
60654                 newCell = walk(r, c+1, 1);
60655                 break;
60656             
60657             case e.LEFT:
60658                 newCell = walk(r, c-1, -1);
60659                 break;
60660             
60661             case e.ENTER:
60662                 
60663                 if(g.isEditor && !g.editing){
60664                    g.startEditing(r, c);
60665                    e.stopEvent();
60666                    return;
60667                 }
60668                 
60669                 
60670              break;
60671         };
60672         if(newCell){
60673             this.select(newCell[0], newCell[1]);
60674             e.stopEvent();
60675             
60676         }
60677     },
60678
60679     acceptsNav : function(row, col, cm){
60680         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60681     },
60682     /**
60683      * Selects a cell.
60684      * @param {Number} field (not used) - as it's normally used as a listener
60685      * @param {Number} e - event - fake it by using
60686      *
60687      * var e = Roo.EventObjectImpl.prototype;
60688      * e.keyCode = e.TAB
60689      *
60690      * 
60691      */
60692     onEditorKey : function(field, e){
60693         
60694         var k = e.getKey(),
60695             newCell,
60696             g = this.grid,
60697             ed = g.activeEditor,
60698             forward = false;
60699         ///Roo.log('onEditorKey' + k);
60700         
60701         
60702         if (this.enter_is_tab && k == e.ENTER) {
60703             k = e.TAB;
60704         }
60705         
60706         if(k == e.TAB){
60707             if(e.shiftKey){
60708                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60709             }else{
60710                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60711                 forward = true;
60712             }
60713             
60714             e.stopEvent();
60715             
60716         } else if(k == e.ENTER &&  !e.ctrlKey){
60717             ed.completeEdit();
60718             e.stopEvent();
60719             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60720         
60721                 } else if(k == e.ESC){
60722             ed.cancelEdit();
60723         }
60724                 
60725         if (newCell) {
60726             var ecall = { cell : newCell, forward : forward };
60727             this.fireEvent('beforeeditnext', ecall );
60728             newCell = ecall.cell;
60729                         forward = ecall.forward;
60730         }
60731                 
60732         if(newCell){
60733             //Roo.log('next cell after edit');
60734             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
60735         } else if (forward) {
60736             // tabbed past last
60737             this.fireEvent.defer(100, this, ['tabend',this]);
60738         }
60739     }
60740 });/*
60741  * Based on:
60742  * Ext JS Library 1.1.1
60743  * Copyright(c) 2006-2007, Ext JS, LLC.
60744  *
60745  * Originally Released Under LGPL - original licence link has changed is not relivant.
60746  *
60747  * Fork - LGPL
60748  * <script type="text/javascript">
60749  */
60750  
60751 /**
60752  * @class Roo.grid.EditorGrid
60753  * @extends Roo.grid.Grid
60754  * Class for creating and editable grid.
60755  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
60756  * The container MUST have some type of size defined for the grid to fill. The container will be 
60757  * automatically set to position relative if it isn't already.
60758  * @param {Object} dataSource The data model to bind to
60759  * @param {Object} colModel The column model with info about this grid's columns
60760  */
60761 Roo.grid.EditorGrid = function(container, config){
60762     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
60763     this.getGridEl().addClass("xedit-grid");
60764
60765     if(!this.selModel){
60766         this.selModel = new Roo.grid.CellSelectionModel();
60767     }
60768
60769     this.activeEditor = null;
60770
60771         this.addEvents({
60772             /**
60773              * @event beforeedit
60774              * Fires before cell editing is triggered. The edit event object has the following properties <br />
60775              * <ul style="padding:5px;padding-left:16px;">
60776              * <li>grid - This grid</li>
60777              * <li>record - The record being edited</li>
60778              * <li>field - The field name being edited</li>
60779              * <li>value - The value for the field being edited.</li>
60780              * <li>row - The grid row index</li>
60781              * <li>column - The grid column index</li>
60782              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60783              * </ul>
60784              * @param {Object} e An edit event (see above for description)
60785              */
60786             "beforeedit" : true,
60787             /**
60788              * @event afteredit
60789              * Fires after a cell is edited. <br />
60790              * <ul style="padding:5px;padding-left:16px;">
60791              * <li>grid - This grid</li>
60792              * <li>record - The record being edited</li>
60793              * <li>field - The field name being edited</li>
60794              * <li>value - The value being set</li>
60795              * <li>originalValue - The original value for the field, before the edit.</li>
60796              * <li>row - The grid row index</li>
60797              * <li>column - The grid column index</li>
60798              * </ul>
60799              * @param {Object} e An edit event (see above for description)
60800              */
60801             "afteredit" : true,
60802             /**
60803              * @event validateedit
60804              * Fires after a cell is edited, but before the value is set in the record. 
60805          * You can use this to modify the value being set in the field, Return false
60806              * to cancel the change. The edit event object has the following properties <br />
60807              * <ul style="padding:5px;padding-left:16px;">
60808          * <li>editor - This editor</li>
60809              * <li>grid - This grid</li>
60810              * <li>record - The record being edited</li>
60811              * <li>field - The field name being edited</li>
60812              * <li>value - The value being set</li>
60813              * <li>originalValue - The original value for the field, before the edit.</li>
60814              * <li>row - The grid row index</li>
60815              * <li>column - The grid column index</li>
60816              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
60817              * </ul>
60818              * @param {Object} e An edit event (see above for description)
60819              */
60820             "validateedit" : true
60821         });
60822     this.on("bodyscroll", this.stopEditing,  this);
60823     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
60824 };
60825
60826 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
60827     /**
60828      * @cfg {Number} clicksToEdit
60829      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
60830      */
60831     clicksToEdit: 2,
60832
60833     // private
60834     isEditor : true,
60835     // private
60836     trackMouseOver: false, // causes very odd FF errors
60837
60838     onCellDblClick : function(g, row, col){
60839         this.startEditing(row, col);
60840     },
60841
60842     onEditComplete : function(ed, value, startValue){
60843         this.editing = false;
60844         this.activeEditor = null;
60845         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
60846         var r = ed.record;
60847         var field = this.colModel.getDataIndex(ed.col);
60848         var e = {
60849             grid: this,
60850             record: r,
60851             field: field,
60852             originalValue: startValue,
60853             value: value,
60854             row: ed.row,
60855             column: ed.col,
60856             cancel:false,
60857             editor: ed
60858         };
60859         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
60860         cell.show();
60861           
60862         if(String(value) !== String(startValue)){
60863             
60864             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
60865                 r.set(field, e.value);
60866                 // if we are dealing with a combo box..
60867                 // then we also set the 'name' colum to be the displayField
60868                 if (ed.field.displayField && ed.field.name) {
60869                     r.set(ed.field.name, ed.field.el.dom.value);
60870                 }
60871                 
60872                 delete e.cancel; //?? why!!!
60873                 this.fireEvent("afteredit", e);
60874             }
60875         } else {
60876             this.fireEvent("afteredit", e); // always fire it!
60877         }
60878         this.view.focusCell(ed.row, ed.col);
60879     },
60880
60881     /**
60882      * Starts editing the specified for the specified row/column
60883      * @param {Number} rowIndex
60884      * @param {Number} colIndex
60885      */
60886     startEditing : function(row, col){
60887         this.stopEditing();
60888         if(this.colModel.isCellEditable(col, row)){
60889             this.view.ensureVisible(row, col, true);
60890           
60891             var r = this.dataSource.getAt(row);
60892             var field = this.colModel.getDataIndex(col);
60893             var cell = Roo.get(this.view.getCell(row,col));
60894             var e = {
60895                 grid: this,
60896                 record: r,
60897                 field: field,
60898                 value: r.data[field],
60899                 row: row,
60900                 column: col,
60901                 cancel:false 
60902             };
60903             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
60904                 this.editing = true;
60905                 var ed = this.colModel.getCellEditor(col, row);
60906                 
60907                 if (!ed) {
60908                     return;
60909                 }
60910                 if(!ed.rendered){
60911                     ed.render(ed.parentEl || document.body);
60912                 }
60913                 ed.field.reset();
60914                
60915                 cell.hide();
60916                 
60917                 (function(){ // complex but required for focus issues in safari, ie and opera
60918                     ed.row = row;
60919                     ed.col = col;
60920                     ed.record = r;
60921                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
60922                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
60923                     this.activeEditor = ed;
60924                     var v = r.data[field];
60925                     ed.startEdit(this.view.getCell(row, col), v);
60926                     // combo's with 'displayField and name set
60927                     if (ed.field.displayField && ed.field.name) {
60928                         ed.field.el.dom.value = r.data[ed.field.name];
60929                     }
60930                     
60931                     
60932                 }).defer(50, this);
60933             }
60934         }
60935     },
60936         
60937     /**
60938      * Stops any active editing
60939      */
60940     stopEditing : function(){
60941         if(this.activeEditor){
60942             this.activeEditor.completeEdit();
60943         }
60944         this.activeEditor = null;
60945     },
60946         
60947          /**
60948      * Called to get grid's drag proxy text, by default returns this.ddText.
60949      * @return {String}
60950      */
60951     getDragDropText : function(){
60952         var count = this.selModel.getSelectedCell() ? 1 : 0;
60953         return String.format(this.ddText, count, count == 1 ? '' : 's');
60954     }
60955         
60956 });/*
60957  * Based on:
60958  * Ext JS Library 1.1.1
60959  * Copyright(c) 2006-2007, Ext JS, LLC.
60960  *
60961  * Originally Released Under LGPL - original licence link has changed is not relivant.
60962  *
60963  * Fork - LGPL
60964  * <script type="text/javascript">
60965  */
60966
60967 // private - not really -- you end up using it !
60968 // This is a support class used internally by the Grid components
60969
60970 /**
60971  * @class Roo.grid.GridEditor
60972  * @extends Roo.Editor
60973  * Class for creating and editable grid elements.
60974  * @param {Object} config any settings (must include field)
60975  */
60976 Roo.grid.GridEditor = function(field, config){
60977     if (!config && field.field) {
60978         config = field;
60979         field = Roo.factory(config.field, Roo.form);
60980     }
60981     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
60982     field.monitorTab = false;
60983 };
60984
60985 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
60986     
60987     /**
60988      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
60989      */
60990     
60991     alignment: "tl-tl",
60992     autoSize: "width",
60993     hideEl : false,
60994     cls: "x-small-editor x-grid-editor",
60995     shim:false,
60996     shadow:"frame"
60997 });/*
60998  * Based on:
60999  * Ext JS Library 1.1.1
61000  * Copyright(c) 2006-2007, Ext JS, LLC.
61001  *
61002  * Originally Released Under LGPL - original licence link has changed is not relivant.
61003  *
61004  * Fork - LGPL
61005  * <script type="text/javascript">
61006  */
61007   
61008
61009   
61010 Roo.grid.PropertyRecord = Roo.data.Record.create([
61011     {name:'name',type:'string'},  'value'
61012 ]);
61013
61014
61015 Roo.grid.PropertyStore = function(grid, source){
61016     this.grid = grid;
61017     this.store = new Roo.data.Store({
61018         recordType : Roo.grid.PropertyRecord
61019     });
61020     this.store.on('update', this.onUpdate,  this);
61021     if(source){
61022         this.setSource(source);
61023     }
61024     Roo.grid.PropertyStore.superclass.constructor.call(this);
61025 };
61026
61027
61028
61029 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61030     setSource : function(o){
61031         this.source = o;
61032         this.store.removeAll();
61033         var data = [];
61034         for(var k in o){
61035             if(this.isEditableValue(o[k])){
61036                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61037             }
61038         }
61039         this.store.loadRecords({records: data}, {}, true);
61040     },
61041
61042     onUpdate : function(ds, record, type){
61043         if(type == Roo.data.Record.EDIT){
61044             var v = record.data['value'];
61045             var oldValue = record.modified['value'];
61046             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61047                 this.source[record.id] = v;
61048                 record.commit();
61049                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61050             }else{
61051                 record.reject();
61052             }
61053         }
61054     },
61055
61056     getProperty : function(row){
61057        return this.store.getAt(row);
61058     },
61059
61060     isEditableValue: function(val){
61061         if(val && val instanceof Date){
61062             return true;
61063         }else if(typeof val == 'object' || typeof val == 'function'){
61064             return false;
61065         }
61066         return true;
61067     },
61068
61069     setValue : function(prop, value){
61070         this.source[prop] = value;
61071         this.store.getById(prop).set('value', value);
61072     },
61073
61074     getSource : function(){
61075         return this.source;
61076     }
61077 });
61078
61079 Roo.grid.PropertyColumnModel = function(grid, store){
61080     this.grid = grid;
61081     var g = Roo.grid;
61082     g.PropertyColumnModel.superclass.constructor.call(this, [
61083         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61084         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61085     ]);
61086     this.store = store;
61087     this.bselect = Roo.DomHelper.append(document.body, {
61088         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61089             {tag: 'option', value: 'true', html: 'true'},
61090             {tag: 'option', value: 'false', html: 'false'}
61091         ]
61092     });
61093     Roo.id(this.bselect);
61094     var f = Roo.form;
61095     this.editors = {
61096         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61097         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61098         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61099         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61100         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61101     };
61102     this.renderCellDelegate = this.renderCell.createDelegate(this);
61103     this.renderPropDelegate = this.renderProp.createDelegate(this);
61104 };
61105
61106 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61107     
61108     
61109     nameText : 'Name',
61110     valueText : 'Value',
61111     
61112     dateFormat : 'm/j/Y',
61113     
61114     
61115     renderDate : function(dateVal){
61116         return dateVal.dateFormat(this.dateFormat);
61117     },
61118
61119     renderBool : function(bVal){
61120         return bVal ? 'true' : 'false';
61121     },
61122
61123     isCellEditable : function(colIndex, rowIndex){
61124         return colIndex == 1;
61125     },
61126
61127     getRenderer : function(col){
61128         return col == 1 ?
61129             this.renderCellDelegate : this.renderPropDelegate;
61130     },
61131
61132     renderProp : function(v){
61133         return this.getPropertyName(v);
61134     },
61135
61136     renderCell : function(val){
61137         var rv = val;
61138         if(val instanceof Date){
61139             rv = this.renderDate(val);
61140         }else if(typeof val == 'boolean'){
61141             rv = this.renderBool(val);
61142         }
61143         return Roo.util.Format.htmlEncode(rv);
61144     },
61145
61146     getPropertyName : function(name){
61147         var pn = this.grid.propertyNames;
61148         return pn && pn[name] ? pn[name] : name;
61149     },
61150
61151     getCellEditor : function(colIndex, rowIndex){
61152         var p = this.store.getProperty(rowIndex);
61153         var n = p.data['name'], val = p.data['value'];
61154         
61155         if(typeof(this.grid.customEditors[n]) == 'string'){
61156             return this.editors[this.grid.customEditors[n]];
61157         }
61158         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61159             return this.grid.customEditors[n];
61160         }
61161         if(val instanceof Date){
61162             return this.editors['date'];
61163         }else if(typeof val == 'number'){
61164             return this.editors['number'];
61165         }else if(typeof val == 'boolean'){
61166             return this.editors['boolean'];
61167         }else{
61168             return this.editors['string'];
61169         }
61170     }
61171 });
61172
61173 /**
61174  * @class Roo.grid.PropertyGrid
61175  * @extends Roo.grid.EditorGrid
61176  * This class represents the  interface of a component based property grid control.
61177  * <br><br>Usage:<pre><code>
61178  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61179       
61180  });
61181  // set any options
61182  grid.render();
61183  * </code></pre>
61184   
61185  * @constructor
61186  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61187  * The container MUST have some type of size defined for the grid to fill. The container will be
61188  * automatically set to position relative if it isn't already.
61189  * @param {Object} config A config object that sets properties on this grid.
61190  */
61191 Roo.grid.PropertyGrid = function(container, config){
61192     config = config || {};
61193     var store = new Roo.grid.PropertyStore(this);
61194     this.store = store;
61195     var cm = new Roo.grid.PropertyColumnModel(this, store);
61196     store.store.sort('name', 'ASC');
61197     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
61198         ds: store.store,
61199         cm: cm,
61200         enableColLock:false,
61201         enableColumnMove:false,
61202         stripeRows:false,
61203         trackMouseOver: false,
61204         clicksToEdit:1
61205     }, config));
61206     this.getGridEl().addClass('x-props-grid');
61207     this.lastEditRow = null;
61208     this.on('columnresize', this.onColumnResize, this);
61209     this.addEvents({
61210          /**
61211              * @event beforepropertychange
61212              * Fires before a property changes (return false to stop?)
61213              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61214              * @param {String} id Record Id
61215              * @param {String} newval New Value
61216          * @param {String} oldval Old Value
61217              */
61218         "beforepropertychange": true,
61219         /**
61220              * @event propertychange
61221              * Fires after a property changes
61222              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61223              * @param {String} id Record Id
61224              * @param {String} newval New Value
61225          * @param {String} oldval Old Value
61226              */
61227         "propertychange": true
61228     });
61229     this.customEditors = this.customEditors || {};
61230 };
61231 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61232     
61233      /**
61234      * @cfg {Object} customEditors map of colnames=> custom editors.
61235      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61236      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61237      * false disables editing of the field.
61238          */
61239     
61240       /**
61241      * @cfg {Object} propertyNames map of property Names to their displayed value
61242          */
61243     
61244     render : function(){
61245         Roo.grid.PropertyGrid.superclass.render.call(this);
61246         this.autoSize.defer(100, this);
61247     },
61248
61249     autoSize : function(){
61250         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61251         if(this.view){
61252             this.view.fitColumns();
61253         }
61254     },
61255
61256     onColumnResize : function(){
61257         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61258         this.autoSize();
61259     },
61260     /**
61261      * Sets the data for the Grid
61262      * accepts a Key => Value object of all the elements avaiable.
61263      * @param {Object} data  to appear in grid.
61264      */
61265     setSource : function(source){
61266         this.store.setSource(source);
61267         //this.autoSize();
61268     },
61269     /**
61270      * Gets all the data from the grid.
61271      * @return {Object} data  data stored in grid
61272      */
61273     getSource : function(){
61274         return this.store.getSource();
61275     }
61276 });/*
61277   
61278  * Licence LGPL
61279  
61280  */
61281  
61282 /**
61283  * @class Roo.grid.Calendar
61284  * @extends Roo.grid.Grid
61285  * This class extends the Grid to provide a calendar widget
61286  * <br><br>Usage:<pre><code>
61287  var grid = new Roo.grid.Calendar("my-container-id", {
61288      ds: myDataStore,
61289      cm: myColModel,
61290      selModel: mySelectionModel,
61291      autoSizeColumns: true,
61292      monitorWindowResize: false,
61293      trackMouseOver: true
61294      eventstore : real data store..
61295  });
61296  // set any options
61297  grid.render();
61298   
61299   * @constructor
61300  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61301  * The container MUST have some type of size defined for the grid to fill. The container will be
61302  * automatically set to position relative if it isn't already.
61303  * @param {Object} config A config object that sets properties on this grid.
61304  */
61305 Roo.grid.Calendar = function(container, config){
61306         // initialize the container
61307         this.container = Roo.get(container);
61308         this.container.update("");
61309         this.container.setStyle("overflow", "hidden");
61310     this.container.addClass('x-grid-container');
61311
61312     this.id = this.container.id;
61313
61314     Roo.apply(this, config);
61315     // check and correct shorthanded configs
61316     
61317     var rows = [];
61318     var d =1;
61319     for (var r = 0;r < 6;r++) {
61320         
61321         rows[r]=[];
61322         for (var c =0;c < 7;c++) {
61323             rows[r][c]= '';
61324         }
61325     }
61326     if (this.eventStore) {
61327         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61328         this.eventStore.on('load',this.onLoad, this);
61329         this.eventStore.on('beforeload',this.clearEvents, this);
61330          
61331     }
61332     
61333     this.dataSource = new Roo.data.Store({
61334             proxy: new Roo.data.MemoryProxy(rows),
61335             reader: new Roo.data.ArrayReader({}, [
61336                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61337     });
61338
61339     this.dataSource.load();
61340     this.ds = this.dataSource;
61341     this.ds.xmodule = this.xmodule || false;
61342     
61343     
61344     var cellRender = function(v,x,r)
61345     {
61346         return String.format(
61347             '<div class="fc-day  fc-widget-content"><div>' +
61348                 '<div class="fc-event-container"></div>' +
61349                 '<div class="fc-day-number">{0}</div>'+
61350                 
61351                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61352             '</div></div>', v);
61353     
61354     }
61355     
61356     
61357     this.colModel = new Roo.grid.ColumnModel( [
61358         {
61359             xtype: 'ColumnModel',
61360             xns: Roo.grid,
61361             dataIndex : 'weekday0',
61362             header : 'Sunday',
61363             renderer : cellRender
61364         },
61365         {
61366             xtype: 'ColumnModel',
61367             xns: Roo.grid,
61368             dataIndex : 'weekday1',
61369             header : 'Monday',
61370             renderer : cellRender
61371         },
61372         {
61373             xtype: 'ColumnModel',
61374             xns: Roo.grid,
61375             dataIndex : 'weekday2',
61376             header : 'Tuesday',
61377             renderer : cellRender
61378         },
61379         {
61380             xtype: 'ColumnModel',
61381             xns: Roo.grid,
61382             dataIndex : 'weekday3',
61383             header : 'Wednesday',
61384             renderer : cellRender
61385         },
61386         {
61387             xtype: 'ColumnModel',
61388             xns: Roo.grid,
61389             dataIndex : 'weekday4',
61390             header : 'Thursday',
61391             renderer : cellRender
61392         },
61393         {
61394             xtype: 'ColumnModel',
61395             xns: Roo.grid,
61396             dataIndex : 'weekday5',
61397             header : 'Friday',
61398             renderer : cellRender
61399         },
61400         {
61401             xtype: 'ColumnModel',
61402             xns: Roo.grid,
61403             dataIndex : 'weekday6',
61404             header : 'Saturday',
61405             renderer : cellRender
61406         }
61407     ]);
61408     this.cm = this.colModel;
61409     this.cm.xmodule = this.xmodule || false;
61410  
61411         
61412           
61413     //this.selModel = new Roo.grid.CellSelectionModel();
61414     //this.sm = this.selModel;
61415     //this.selModel.init(this);
61416     
61417     
61418     if(this.width){
61419         this.container.setWidth(this.width);
61420     }
61421
61422     if(this.height){
61423         this.container.setHeight(this.height);
61424     }
61425     /** @private */
61426         this.addEvents({
61427         // raw events
61428         /**
61429          * @event click
61430          * The raw click event for the entire grid.
61431          * @param {Roo.EventObject} e
61432          */
61433         "click" : true,
61434         /**
61435          * @event dblclick
61436          * The raw dblclick event for the entire grid.
61437          * @param {Roo.EventObject} e
61438          */
61439         "dblclick" : true,
61440         /**
61441          * @event contextmenu
61442          * The raw contextmenu event for the entire grid.
61443          * @param {Roo.EventObject} e
61444          */
61445         "contextmenu" : true,
61446         /**
61447          * @event mousedown
61448          * The raw mousedown event for the entire grid.
61449          * @param {Roo.EventObject} e
61450          */
61451         "mousedown" : true,
61452         /**
61453          * @event mouseup
61454          * The raw mouseup event for the entire grid.
61455          * @param {Roo.EventObject} e
61456          */
61457         "mouseup" : true,
61458         /**
61459          * @event mouseover
61460          * The raw mouseover event for the entire grid.
61461          * @param {Roo.EventObject} e
61462          */
61463         "mouseover" : true,
61464         /**
61465          * @event mouseout
61466          * The raw mouseout event for the entire grid.
61467          * @param {Roo.EventObject} e
61468          */
61469         "mouseout" : true,
61470         /**
61471          * @event keypress
61472          * The raw keypress event for the entire grid.
61473          * @param {Roo.EventObject} e
61474          */
61475         "keypress" : true,
61476         /**
61477          * @event keydown
61478          * The raw keydown event for the entire grid.
61479          * @param {Roo.EventObject} e
61480          */
61481         "keydown" : true,
61482
61483         // custom events
61484
61485         /**
61486          * @event cellclick
61487          * Fires when a cell is clicked
61488          * @param {Grid} this
61489          * @param {Number} rowIndex
61490          * @param {Number} columnIndex
61491          * @param {Roo.EventObject} e
61492          */
61493         "cellclick" : true,
61494         /**
61495          * @event celldblclick
61496          * Fires when a cell is double clicked
61497          * @param {Grid} this
61498          * @param {Number} rowIndex
61499          * @param {Number} columnIndex
61500          * @param {Roo.EventObject} e
61501          */
61502         "celldblclick" : true,
61503         /**
61504          * @event rowclick
61505          * Fires when a row is clicked
61506          * @param {Grid} this
61507          * @param {Number} rowIndex
61508          * @param {Roo.EventObject} e
61509          */
61510         "rowclick" : true,
61511         /**
61512          * @event rowdblclick
61513          * Fires when a row is double clicked
61514          * @param {Grid} this
61515          * @param {Number} rowIndex
61516          * @param {Roo.EventObject} e
61517          */
61518         "rowdblclick" : true,
61519         /**
61520          * @event headerclick
61521          * Fires when a header is clicked
61522          * @param {Grid} this
61523          * @param {Number} columnIndex
61524          * @param {Roo.EventObject} e
61525          */
61526         "headerclick" : true,
61527         /**
61528          * @event headerdblclick
61529          * Fires when a header cell is double clicked
61530          * @param {Grid} this
61531          * @param {Number} columnIndex
61532          * @param {Roo.EventObject} e
61533          */
61534         "headerdblclick" : true,
61535         /**
61536          * @event rowcontextmenu
61537          * Fires when a row is right clicked
61538          * @param {Grid} this
61539          * @param {Number} rowIndex
61540          * @param {Roo.EventObject} e
61541          */
61542         "rowcontextmenu" : true,
61543         /**
61544          * @event cellcontextmenu
61545          * Fires when a cell is right clicked
61546          * @param {Grid} this
61547          * @param {Number} rowIndex
61548          * @param {Number} cellIndex
61549          * @param {Roo.EventObject} e
61550          */
61551          "cellcontextmenu" : true,
61552         /**
61553          * @event headercontextmenu
61554          * Fires when a header is right clicked
61555          * @param {Grid} this
61556          * @param {Number} columnIndex
61557          * @param {Roo.EventObject} e
61558          */
61559         "headercontextmenu" : true,
61560         /**
61561          * @event bodyscroll
61562          * Fires when the body element is scrolled
61563          * @param {Number} scrollLeft
61564          * @param {Number} scrollTop
61565          */
61566         "bodyscroll" : true,
61567         /**
61568          * @event columnresize
61569          * Fires when the user resizes a column
61570          * @param {Number} columnIndex
61571          * @param {Number} newSize
61572          */
61573         "columnresize" : true,
61574         /**
61575          * @event columnmove
61576          * Fires when the user moves a column
61577          * @param {Number} oldIndex
61578          * @param {Number} newIndex
61579          */
61580         "columnmove" : true,
61581         /**
61582          * @event startdrag
61583          * Fires when row(s) start being dragged
61584          * @param {Grid} this
61585          * @param {Roo.GridDD} dd The drag drop object
61586          * @param {event} e The raw browser event
61587          */
61588         "startdrag" : true,
61589         /**
61590          * @event enddrag
61591          * Fires when a drag operation is complete
61592          * @param {Grid} this
61593          * @param {Roo.GridDD} dd The drag drop object
61594          * @param {event} e The raw browser event
61595          */
61596         "enddrag" : true,
61597         /**
61598          * @event dragdrop
61599          * Fires when dragged row(s) are dropped on a valid DD target
61600          * @param {Grid} this
61601          * @param {Roo.GridDD} dd The drag drop object
61602          * @param {String} targetId The target drag drop object
61603          * @param {event} e The raw browser event
61604          */
61605         "dragdrop" : true,
61606         /**
61607          * @event dragover
61608          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61609          * @param {Grid} this
61610          * @param {Roo.GridDD} dd The drag drop object
61611          * @param {String} targetId The target drag drop object
61612          * @param {event} e The raw browser event
61613          */
61614         "dragover" : true,
61615         /**
61616          * @event dragenter
61617          *  Fires when the dragged row(s) first cross another DD target while being dragged
61618          * @param {Grid} this
61619          * @param {Roo.GridDD} dd The drag drop object
61620          * @param {String} targetId The target drag drop object
61621          * @param {event} e The raw browser event
61622          */
61623         "dragenter" : true,
61624         /**
61625          * @event dragout
61626          * Fires when the dragged row(s) leave another DD target while being dragged
61627          * @param {Grid} this
61628          * @param {Roo.GridDD} dd The drag drop object
61629          * @param {String} targetId The target drag drop object
61630          * @param {event} e The raw browser event
61631          */
61632         "dragout" : true,
61633         /**
61634          * @event rowclass
61635          * Fires when a row is rendered, so you can change add a style to it.
61636          * @param {GridView} gridview   The grid view
61637          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61638          */
61639         'rowclass' : true,
61640
61641         /**
61642          * @event render
61643          * Fires when the grid is rendered
61644          * @param {Grid} grid
61645          */
61646         'render' : true,
61647             /**
61648              * @event select
61649              * Fires when a date is selected
61650              * @param {DatePicker} this
61651              * @param {Date} date The selected date
61652              */
61653         'select': true,
61654         /**
61655              * @event monthchange
61656              * Fires when the displayed month changes 
61657              * @param {DatePicker} this
61658              * @param {Date} date The selected month
61659              */
61660         'monthchange': true,
61661         /**
61662              * @event evententer
61663              * Fires when mouse over an event
61664              * @param {Calendar} this
61665              * @param {event} Event
61666              */
61667         'evententer': true,
61668         /**
61669              * @event eventleave
61670              * Fires when the mouse leaves an
61671              * @param {Calendar} this
61672              * @param {event}
61673              */
61674         'eventleave': true,
61675         /**
61676              * @event eventclick
61677              * Fires when the mouse click an
61678              * @param {Calendar} this
61679              * @param {event}
61680              */
61681         'eventclick': true,
61682         /**
61683              * @event eventrender
61684              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
61685              * @param {Calendar} this
61686              * @param {data} data to be modified
61687              */
61688         'eventrender': true
61689         
61690     });
61691
61692     Roo.grid.Grid.superclass.constructor.call(this);
61693     this.on('render', function() {
61694         this.view.el.addClass('x-grid-cal'); 
61695         
61696         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
61697
61698     },this);
61699     
61700     if (!Roo.grid.Calendar.style) {
61701         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
61702             
61703             
61704             '.x-grid-cal .x-grid-col' :  {
61705                 height: 'auto !important',
61706                 'vertical-align': 'top'
61707             },
61708             '.x-grid-cal  .fc-event-hori' : {
61709                 height: '14px'
61710             }
61711              
61712             
61713         }, Roo.id());
61714     }
61715
61716     
61717     
61718 };
61719 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
61720     /**
61721      * @cfg {Store} eventStore The store that loads events.
61722      */
61723     eventStore : 25,
61724
61725      
61726     activeDate : false,
61727     startDay : 0,
61728     autoWidth : true,
61729     monitorWindowResize : false,
61730
61731     
61732     resizeColumns : function() {
61733         var col = (this.view.el.getWidth() / 7) - 3;
61734         // loop through cols, and setWidth
61735         for(var i =0 ; i < 7 ; i++){
61736             this.cm.setColumnWidth(i, col);
61737         }
61738     },
61739      setDate :function(date) {
61740         
61741         Roo.log('setDate?');
61742         
61743         this.resizeColumns();
61744         var vd = this.activeDate;
61745         this.activeDate = date;
61746 //        if(vd && this.el){
61747 //            var t = date.getTime();
61748 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
61749 //                Roo.log('using add remove');
61750 //                
61751 //                this.fireEvent('monthchange', this, date);
61752 //                
61753 //                this.cells.removeClass("fc-state-highlight");
61754 //                this.cells.each(function(c){
61755 //                   if(c.dateValue == t){
61756 //                       c.addClass("fc-state-highlight");
61757 //                       setTimeout(function(){
61758 //                            try{c.dom.firstChild.focus();}catch(e){}
61759 //                       }, 50);
61760 //                       return false;
61761 //                   }
61762 //                   return true;
61763 //                });
61764 //                return;
61765 //            }
61766 //        }
61767         
61768         var days = date.getDaysInMonth();
61769         
61770         var firstOfMonth = date.getFirstDateOfMonth();
61771         var startingPos = firstOfMonth.getDay()-this.startDay;
61772         
61773         if(startingPos < this.startDay){
61774             startingPos += 7;
61775         }
61776         
61777         var pm = date.add(Date.MONTH, -1);
61778         var prevStart = pm.getDaysInMonth()-startingPos;
61779 //        
61780         
61781         
61782         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61783         
61784         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
61785         //this.cells.addClassOnOver('fc-state-hover');
61786         
61787         var cells = this.cells.elements;
61788         var textEls = this.textNodes;
61789         
61790         //Roo.each(cells, function(cell){
61791         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
61792         //});
61793         
61794         days += startingPos;
61795
61796         // convert everything to numbers so it's fast
61797         var day = 86400000;
61798         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
61799         //Roo.log(d);
61800         //Roo.log(pm);
61801         //Roo.log(prevStart);
61802         
61803         var today = new Date().clearTime().getTime();
61804         var sel = date.clearTime().getTime();
61805         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
61806         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
61807         var ddMatch = this.disabledDatesRE;
61808         var ddText = this.disabledDatesText;
61809         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
61810         var ddaysText = this.disabledDaysText;
61811         var format = this.format;
61812         
61813         var setCellClass = function(cal, cell){
61814             
61815             //Roo.log('set Cell Class');
61816             cell.title = "";
61817             var t = d.getTime();
61818             
61819             //Roo.log(d);
61820             
61821             
61822             cell.dateValue = t;
61823             if(t == today){
61824                 cell.className += " fc-today";
61825                 cell.className += " fc-state-highlight";
61826                 cell.title = cal.todayText;
61827             }
61828             if(t == sel){
61829                 // disable highlight in other month..
61830                 cell.className += " fc-state-highlight";
61831                 
61832             }
61833             // disabling
61834             if(t < min) {
61835                 //cell.className = " fc-state-disabled";
61836                 cell.title = cal.minText;
61837                 return;
61838             }
61839             if(t > max) {
61840                 //cell.className = " fc-state-disabled";
61841                 cell.title = cal.maxText;
61842                 return;
61843             }
61844             if(ddays){
61845                 if(ddays.indexOf(d.getDay()) != -1){
61846                     // cell.title = ddaysText;
61847                    // cell.className = " fc-state-disabled";
61848                 }
61849             }
61850             if(ddMatch && format){
61851                 var fvalue = d.dateFormat(format);
61852                 if(ddMatch.test(fvalue)){
61853                     cell.title = ddText.replace("%0", fvalue);
61854                    cell.className = " fc-state-disabled";
61855                 }
61856             }
61857             
61858             if (!cell.initialClassName) {
61859                 cell.initialClassName = cell.dom.className;
61860             }
61861             
61862             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
61863         };
61864
61865         var i = 0;
61866         
61867         for(; i < startingPos; i++) {
61868             cells[i].dayName =  (++prevStart);
61869             Roo.log(textEls[i]);
61870             d.setDate(d.getDate()+1);
61871             
61872             //cells[i].className = "fc-past fc-other-month";
61873             setCellClass(this, cells[i]);
61874         }
61875         
61876         var intDay = 0;
61877         
61878         for(; i < days; i++){
61879             intDay = i - startingPos + 1;
61880             cells[i].dayName =  (intDay);
61881             d.setDate(d.getDate()+1);
61882             
61883             cells[i].className = ''; // "x-date-active";
61884             setCellClass(this, cells[i]);
61885         }
61886         var extraDays = 0;
61887         
61888         for(; i < 42; i++) {
61889             //textEls[i].innerHTML = (++extraDays);
61890             
61891             d.setDate(d.getDate()+1);
61892             cells[i].dayName = (++extraDays);
61893             cells[i].className = "fc-future fc-other-month";
61894             setCellClass(this, cells[i]);
61895         }
61896         
61897         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
61898         
61899         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
61900         
61901         // this will cause all the cells to mis
61902         var rows= [];
61903         var i =0;
61904         for (var r = 0;r < 6;r++) {
61905             for (var c =0;c < 7;c++) {
61906                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
61907             }    
61908         }
61909         
61910         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
61911         for(i=0;i<cells.length;i++) {
61912             
61913             this.cells.elements[i].dayName = cells[i].dayName ;
61914             this.cells.elements[i].className = cells[i].className;
61915             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
61916             this.cells.elements[i].title = cells[i].title ;
61917             this.cells.elements[i].dateValue = cells[i].dateValue ;
61918         }
61919         
61920         
61921         
61922         
61923         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
61924         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
61925         
61926         ////if(totalRows != 6){
61927             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
61928            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
61929        // }
61930         
61931         this.fireEvent('monthchange', this, date);
61932         
61933         
61934     },
61935  /**
61936      * Returns the grid's SelectionModel.
61937      * @return {SelectionModel}
61938      */
61939     getSelectionModel : function(){
61940         if(!this.selModel){
61941             this.selModel = new Roo.grid.CellSelectionModel();
61942         }
61943         return this.selModel;
61944     },
61945
61946     load: function() {
61947         this.eventStore.load()
61948         
61949         
61950         
61951     },
61952     
61953     findCell : function(dt) {
61954         dt = dt.clearTime().getTime();
61955         var ret = false;
61956         this.cells.each(function(c){
61957             //Roo.log("check " +c.dateValue + '?=' + dt);
61958             if(c.dateValue == dt){
61959                 ret = c;
61960                 return false;
61961             }
61962             return true;
61963         });
61964         
61965         return ret;
61966     },
61967     
61968     findCells : function(rec) {
61969         var s = rec.data.start_dt.clone().clearTime().getTime();
61970        // Roo.log(s);
61971         var e= rec.data.end_dt.clone().clearTime().getTime();
61972        // Roo.log(e);
61973         var ret = [];
61974         this.cells.each(function(c){
61975              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
61976             
61977             if(c.dateValue > e){
61978                 return ;
61979             }
61980             if(c.dateValue < s){
61981                 return ;
61982             }
61983             ret.push(c);
61984         });
61985         
61986         return ret;    
61987     },
61988     
61989     findBestRow: function(cells)
61990     {
61991         var ret = 0;
61992         
61993         for (var i =0 ; i < cells.length;i++) {
61994             ret  = Math.max(cells[i].rows || 0,ret);
61995         }
61996         return ret;
61997         
61998     },
61999     
62000     
62001     addItem : function(rec)
62002     {
62003         // look for vertical location slot in
62004         var cells = this.findCells(rec);
62005         
62006         rec.row = this.findBestRow(cells);
62007         
62008         // work out the location.
62009         
62010         var crow = false;
62011         var rows = [];
62012         for(var i =0; i < cells.length; i++) {
62013             if (!crow) {
62014                 crow = {
62015                     start : cells[i],
62016                     end :  cells[i]
62017                 };
62018                 continue;
62019             }
62020             if (crow.start.getY() == cells[i].getY()) {
62021                 // on same row.
62022                 crow.end = cells[i];
62023                 continue;
62024             }
62025             // different row.
62026             rows.push(crow);
62027             crow = {
62028                 start: cells[i],
62029                 end : cells[i]
62030             };
62031             
62032         }
62033         
62034         rows.push(crow);
62035         rec.els = [];
62036         rec.rows = rows;
62037         rec.cells = cells;
62038         for (var i = 0; i < cells.length;i++) {
62039             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62040             
62041         }
62042         
62043         
62044     },
62045     
62046     clearEvents: function() {
62047         
62048         if (!this.eventStore.getCount()) {
62049             return;
62050         }
62051         // reset number of rows in cells.
62052         Roo.each(this.cells.elements, function(c){
62053             c.rows = 0;
62054         });
62055         
62056         this.eventStore.each(function(e) {
62057             this.clearEvent(e);
62058         },this);
62059         
62060     },
62061     
62062     clearEvent : function(ev)
62063     {
62064         if (ev.els) {
62065             Roo.each(ev.els, function(el) {
62066                 el.un('mouseenter' ,this.onEventEnter, this);
62067                 el.un('mouseleave' ,this.onEventLeave, this);
62068                 el.remove();
62069             },this);
62070             ev.els = [];
62071         }
62072     },
62073     
62074     
62075     renderEvent : function(ev,ctr) {
62076         if (!ctr) {
62077              ctr = this.view.el.select('.fc-event-container',true).first();
62078         }
62079         
62080          
62081         this.clearEvent(ev);
62082             //code
62083        
62084         
62085         
62086         ev.els = [];
62087         var cells = ev.cells;
62088         var rows = ev.rows;
62089         this.fireEvent('eventrender', this, ev);
62090         
62091         for(var i =0; i < rows.length; i++) {
62092             
62093             cls = '';
62094             if (i == 0) {
62095                 cls += ' fc-event-start';
62096             }
62097             if ((i+1) == rows.length) {
62098                 cls += ' fc-event-end';
62099             }
62100             
62101             //Roo.log(ev.data);
62102             // how many rows should it span..
62103             var cg = this.eventTmpl.append(ctr,Roo.apply({
62104                 fccls : cls
62105                 
62106             }, ev.data) , true);
62107             
62108             
62109             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62110             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62111             cg.on('click', this.onEventClick, this, ev);
62112             
62113             ev.els.push(cg);
62114             
62115             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62116             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62117             //Roo.log(cg);
62118              
62119             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62120             cg.setWidth(ebox.right - sbox.x -2);
62121         }
62122     },
62123     
62124     renderEvents: function()
62125     {   
62126         // first make sure there is enough space..
62127         
62128         if (!this.eventTmpl) {
62129             this.eventTmpl = new Roo.Template(
62130                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62131                     '<div class="fc-event-inner">' +
62132                         '<span class="fc-event-time">{time}</span>' +
62133                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62134                     '</div>' +
62135                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62136                 '</div>'
62137             );
62138                 
62139         }
62140                
62141         
62142         
62143         this.cells.each(function(c) {
62144             //Roo.log(c.select('.fc-day-content div',true).first());
62145             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62146         });
62147         
62148         var ctr = this.view.el.select('.fc-event-container',true).first();
62149         
62150         var cls;
62151         this.eventStore.each(function(ev){
62152             
62153             this.renderEvent(ev);
62154              
62155              
62156         }, this);
62157         this.view.layout();
62158         
62159     },
62160     
62161     onEventEnter: function (e, el,event,d) {
62162         this.fireEvent('evententer', this, el, event);
62163     },
62164     
62165     onEventLeave: function (e, el,event,d) {
62166         this.fireEvent('eventleave', this, el, event);
62167     },
62168     
62169     onEventClick: function (e, el,event,d) {
62170         this.fireEvent('eventclick', this, el, event);
62171     },
62172     
62173     onMonthChange: function () {
62174         this.store.load();
62175     },
62176     
62177     onLoad: function () {
62178         
62179         //Roo.log('calendar onload');
62180 //         
62181         if(this.eventStore.getCount() > 0){
62182             
62183            
62184             
62185             this.eventStore.each(function(d){
62186                 
62187                 
62188                 // FIXME..
62189                 var add =   d.data;
62190                 if (typeof(add.end_dt) == 'undefined')  {
62191                     Roo.log("Missing End time in calendar data: ");
62192                     Roo.log(d);
62193                     return;
62194                 }
62195                 if (typeof(add.start_dt) == 'undefined')  {
62196                     Roo.log("Missing Start time in calendar data: ");
62197                     Roo.log(d);
62198                     return;
62199                 }
62200                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62201                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62202                 add.id = add.id || d.id;
62203                 add.title = add.title || '??';
62204                 
62205                 this.addItem(d);
62206                 
62207              
62208             },this);
62209         }
62210         
62211         this.renderEvents();
62212     }
62213     
62214
62215 });
62216 /*
62217  grid : {
62218                 xtype: 'Grid',
62219                 xns: Roo.grid,
62220                 listeners : {
62221                     render : function ()
62222                     {
62223                         _this.grid = this;
62224                         
62225                         if (!this.view.el.hasClass('course-timesheet')) {
62226                             this.view.el.addClass('course-timesheet');
62227                         }
62228                         if (this.tsStyle) {
62229                             this.ds.load({});
62230                             return; 
62231                         }
62232                         Roo.log('width');
62233                         Roo.log(_this.grid.view.el.getWidth());
62234                         
62235                         
62236                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62237                             '.course-timesheet .x-grid-row' : {
62238                                 height: '80px'
62239                             },
62240                             '.x-grid-row td' : {
62241                                 'vertical-align' : 0
62242                             },
62243                             '.course-edit-link' : {
62244                                 'color' : 'blue',
62245                                 'text-overflow' : 'ellipsis',
62246                                 'overflow' : 'hidden',
62247                                 'white-space' : 'nowrap',
62248                                 'cursor' : 'pointer'
62249                             },
62250                             '.sub-link' : {
62251                                 'color' : 'green'
62252                             },
62253                             '.de-act-sup-link' : {
62254                                 'color' : 'purple',
62255                                 'text-decoration' : 'line-through'
62256                             },
62257                             '.de-act-link' : {
62258                                 'color' : 'red',
62259                                 'text-decoration' : 'line-through'
62260                             },
62261                             '.course-timesheet .course-highlight' : {
62262                                 'border-top-style': 'dashed !important',
62263                                 'border-bottom-bottom': 'dashed !important'
62264                             },
62265                             '.course-timesheet .course-item' : {
62266                                 'font-family'   : 'tahoma, arial, helvetica',
62267                                 'font-size'     : '11px',
62268                                 'overflow'      : 'hidden',
62269                                 'padding-left'  : '10px',
62270                                 'padding-right' : '10px',
62271                                 'padding-top' : '10px' 
62272                             }
62273                             
62274                         }, Roo.id());
62275                                 this.ds.load({});
62276                     }
62277                 },
62278                 autoWidth : true,
62279                 monitorWindowResize : false,
62280                 cellrenderer : function(v,x,r)
62281                 {
62282                     return v;
62283                 },
62284                 sm : {
62285                     xtype: 'CellSelectionModel',
62286                     xns: Roo.grid
62287                 },
62288                 dataSource : {
62289                     xtype: 'Store',
62290                     xns: Roo.data,
62291                     listeners : {
62292                         beforeload : function (_self, options)
62293                         {
62294                             options.params = options.params || {};
62295                             options.params._month = _this.monthField.getValue();
62296                             options.params.limit = 9999;
62297                             options.params['sort'] = 'when_dt';    
62298                             options.params['dir'] = 'ASC';    
62299                             this.proxy.loadResponse = this.loadResponse;
62300                             Roo.log("load?");
62301                             //this.addColumns();
62302                         },
62303                         load : function (_self, records, options)
62304                         {
62305                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62306                                 // if you click on the translation.. you can edit it...
62307                                 var el = Roo.get(this);
62308                                 var id = el.dom.getAttribute('data-id');
62309                                 var d = el.dom.getAttribute('data-date');
62310                                 var t = el.dom.getAttribute('data-time');
62311                                 //var id = this.child('span').dom.textContent;
62312                                 
62313                                 //Roo.log(this);
62314                                 Pman.Dialog.CourseCalendar.show({
62315                                     id : id,
62316                                     when_d : d,
62317                                     when_t : t,
62318                                     productitem_active : id ? 1 : 0
62319                                 }, function() {
62320                                     _this.grid.ds.load({});
62321                                 });
62322                            
62323                            });
62324                            
62325                            _this.panel.fireEvent('resize', [ '', '' ]);
62326                         }
62327                     },
62328                     loadResponse : function(o, success, response){
62329                             // this is overridden on before load..
62330                             
62331                             Roo.log("our code?");       
62332                             //Roo.log(success);
62333                             //Roo.log(response)
62334                             delete this.activeRequest;
62335                             if(!success){
62336                                 this.fireEvent("loadexception", this, o, response);
62337                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62338                                 return;
62339                             }
62340                             var result;
62341                             try {
62342                                 result = o.reader.read(response);
62343                             }catch(e){
62344                                 Roo.log("load exception?");
62345                                 this.fireEvent("loadexception", this, o, response, e);
62346                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62347                                 return;
62348                             }
62349                             Roo.log("ready...");        
62350                             // loop through result.records;
62351                             // and set this.tdate[date] = [] << array of records..
62352                             _this.tdata  = {};
62353                             Roo.each(result.records, function(r){
62354                                 //Roo.log(r.data);
62355                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62356                                     _this.tdata[r.data.when_dt.format('j')] = [];
62357                                 }
62358                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62359                             });
62360                             
62361                             //Roo.log(_this.tdata);
62362                             
62363                             result.records = [];
62364                             result.totalRecords = 6;
62365                     
62366                             // let's generate some duumy records for the rows.
62367                             //var st = _this.dateField.getValue();
62368                             
62369                             // work out monday..
62370                             //st = st.add(Date.DAY, -1 * st.format('w'));
62371                             
62372                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62373                             
62374                             var firstOfMonth = date.getFirstDayOfMonth();
62375                             var days = date.getDaysInMonth();
62376                             var d = 1;
62377                             var firstAdded = false;
62378                             for (var i = 0; i < result.totalRecords ; i++) {
62379                                 //var d= st.add(Date.DAY, i);
62380                                 var row = {};
62381                                 var added = 0;
62382                                 for(var w = 0 ; w < 7 ; w++){
62383                                     if(!firstAdded && firstOfMonth != w){
62384                                         continue;
62385                                     }
62386                                     if(d > days){
62387                                         continue;
62388                                     }
62389                                     firstAdded = true;
62390                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62391                                     row['weekday'+w] = String.format(
62392                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62393                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62394                                                     d,
62395                                                     date.format('Y-m-')+dd
62396                                                 );
62397                                     added++;
62398                                     if(typeof(_this.tdata[d]) != 'undefined'){
62399                                         Roo.each(_this.tdata[d], function(r){
62400                                             var is_sub = '';
62401                                             var deactive = '';
62402                                             var id = r.id;
62403                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62404                                             if(r.parent_id*1>0){
62405                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62406                                                 id = r.parent_id;
62407                                             }
62408                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62409                                                 deactive = 'de-act-link';
62410                                             }
62411                                             
62412                                             row['weekday'+w] += String.format(
62413                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62414                                                     id, //0
62415                                                     r.product_id_name, //1
62416                                                     r.when_dt.format('h:ia'), //2
62417                                                     is_sub, //3
62418                                                     deactive, //4
62419                                                     desc // 5
62420                                             );
62421                                         });
62422                                     }
62423                                     d++;
62424                                 }
62425                                 
62426                                 // only do this if something added..
62427                                 if(added > 0){ 
62428                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62429                                 }
62430                                 
62431                                 
62432                                 // push it twice. (second one with an hour..
62433                                 
62434                             }
62435                             //Roo.log(result);
62436                             this.fireEvent("load", this, o, o.request.arg);
62437                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62438                         },
62439                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62440                     proxy : {
62441                         xtype: 'HttpProxy',
62442                         xns: Roo.data,
62443                         method : 'GET',
62444                         url : baseURL + '/Roo/Shop_course.php'
62445                     },
62446                     reader : {
62447                         xtype: 'JsonReader',
62448                         xns: Roo.data,
62449                         id : 'id',
62450                         fields : [
62451                             {
62452                                 'name': 'id',
62453                                 'type': 'int'
62454                             },
62455                             {
62456                                 'name': 'when_dt',
62457                                 'type': 'string'
62458                             },
62459                             {
62460                                 'name': 'end_dt',
62461                                 'type': 'string'
62462                             },
62463                             {
62464                                 'name': 'parent_id',
62465                                 'type': 'int'
62466                             },
62467                             {
62468                                 'name': 'product_id',
62469                                 'type': 'int'
62470                             },
62471                             {
62472                                 'name': 'productitem_id',
62473                                 'type': 'int'
62474                             },
62475                             {
62476                                 'name': 'guid',
62477                                 'type': 'int'
62478                             }
62479                         ]
62480                     }
62481                 },
62482                 toolbar : {
62483                     xtype: 'Toolbar',
62484                     xns: Roo,
62485                     items : [
62486                         {
62487                             xtype: 'Button',
62488                             xns: Roo.Toolbar,
62489                             listeners : {
62490                                 click : function (_self, e)
62491                                 {
62492                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62493                                     sd.setMonth(sd.getMonth()-1);
62494                                     _this.monthField.setValue(sd.format('Y-m-d'));
62495                                     _this.grid.ds.load({});
62496                                 }
62497                             },
62498                             text : "Back"
62499                         },
62500                         {
62501                             xtype: 'Separator',
62502                             xns: Roo.Toolbar
62503                         },
62504                         {
62505                             xtype: 'MonthField',
62506                             xns: Roo.form,
62507                             listeners : {
62508                                 render : function (_self)
62509                                 {
62510                                     _this.monthField = _self;
62511                                    // _this.monthField.set  today
62512                                 },
62513                                 select : function (combo, date)
62514                                 {
62515                                     _this.grid.ds.load({});
62516                                 }
62517                             },
62518                             value : (function() { return new Date(); })()
62519                         },
62520                         {
62521                             xtype: 'Separator',
62522                             xns: Roo.Toolbar
62523                         },
62524                         {
62525                             xtype: 'TextItem',
62526                             xns: Roo.Toolbar,
62527                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62528                         },
62529                         {
62530                             xtype: 'Fill',
62531                             xns: Roo.Toolbar
62532                         },
62533                         {
62534                             xtype: 'Button',
62535                             xns: Roo.Toolbar,
62536                             listeners : {
62537                                 click : function (_self, e)
62538                                 {
62539                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62540                                     sd.setMonth(sd.getMonth()+1);
62541                                     _this.monthField.setValue(sd.format('Y-m-d'));
62542                                     _this.grid.ds.load({});
62543                                 }
62544                             },
62545                             text : "Next"
62546                         }
62547                     ]
62548                 },
62549                  
62550             }
62551         };
62552         
62553         *//*
62554  * Based on:
62555  * Ext JS Library 1.1.1
62556  * Copyright(c) 2006-2007, Ext JS, LLC.
62557  *
62558  * Originally Released Under LGPL - original licence link has changed is not relivant.
62559  *
62560  * Fork - LGPL
62561  * <script type="text/javascript">
62562  */
62563  
62564 /**
62565  * @class Roo.LoadMask
62566  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62567  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62568  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62569  * element's UpdateManager load indicator and will be destroyed after the initial load.
62570  * @constructor
62571  * Create a new LoadMask
62572  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62573  * @param {Object} config The config object
62574  */
62575 Roo.LoadMask = function(el, config){
62576     this.el = Roo.get(el);
62577     Roo.apply(this, config);
62578     if(this.store){
62579         this.store.on('beforeload', this.onBeforeLoad, this);
62580         this.store.on('load', this.onLoad, this);
62581         this.store.on('loadexception', this.onLoadException, this);
62582         this.removeMask = false;
62583     }else{
62584         var um = this.el.getUpdateManager();
62585         um.showLoadIndicator = false; // disable the default indicator
62586         um.on('beforeupdate', this.onBeforeLoad, this);
62587         um.on('update', this.onLoad, this);
62588         um.on('failure', this.onLoad, this);
62589         this.removeMask = true;
62590     }
62591 };
62592
62593 Roo.LoadMask.prototype = {
62594     /**
62595      * @cfg {Boolean} removeMask
62596      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62597      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62598      */
62599     removeMask : false,
62600     /**
62601      * @cfg {String} msg
62602      * The text to display in a centered loading message box (defaults to 'Loading...')
62603      */
62604     msg : 'Loading...',
62605     /**
62606      * @cfg {String} msgCls
62607      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62608      */
62609     msgCls : 'x-mask-loading',
62610
62611     /**
62612      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62613      * @type Boolean
62614      */
62615     disabled: false,
62616
62617     /**
62618      * Disables the mask to prevent it from being displayed
62619      */
62620     disable : function(){
62621        this.disabled = true;
62622     },
62623
62624     /**
62625      * Enables the mask so that it can be displayed
62626      */
62627     enable : function(){
62628         this.disabled = false;
62629     },
62630     
62631     onLoadException : function()
62632     {
62633         Roo.log(arguments);
62634         
62635         if (typeof(arguments[3]) != 'undefined') {
62636             Roo.MessageBox.alert("Error loading",arguments[3]);
62637         } 
62638         /*
62639         try {
62640             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62641                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62642             }   
62643         } catch(e) {
62644             
62645         }
62646         */
62647     
62648         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62649     },
62650     // private
62651     onLoad : function()
62652     {
62653         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62654     },
62655
62656     // private
62657     onBeforeLoad : function(){
62658         if(!this.disabled){
62659             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
62660         }
62661     },
62662
62663     // private
62664     destroy : function(){
62665         if(this.store){
62666             this.store.un('beforeload', this.onBeforeLoad, this);
62667             this.store.un('load', this.onLoad, this);
62668             this.store.un('loadexception', this.onLoadException, this);
62669         }else{
62670             var um = this.el.getUpdateManager();
62671             um.un('beforeupdate', this.onBeforeLoad, this);
62672             um.un('update', this.onLoad, this);
62673             um.un('failure', this.onLoad, this);
62674         }
62675     }
62676 };/*
62677  * Based on:
62678  * Ext JS Library 1.1.1
62679  * Copyright(c) 2006-2007, Ext JS, LLC.
62680  *
62681  * Originally Released Under LGPL - original licence link has changed is not relivant.
62682  *
62683  * Fork - LGPL
62684  * <script type="text/javascript">
62685  */
62686
62687
62688 /**
62689  * @class Roo.XTemplate
62690  * @extends Roo.Template
62691  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
62692 <pre><code>
62693 var t = new Roo.XTemplate(
62694         '&lt;select name="{name}"&gt;',
62695                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
62696         '&lt;/select&gt;'
62697 );
62698  
62699 // then append, applying the master template values
62700  </code></pre>
62701  *
62702  * Supported features:
62703  *
62704  *  Tags:
62705
62706 <pre><code>
62707       {a_variable} - output encoded.
62708       {a_variable.format:("Y-m-d")} - call a method on the variable
62709       {a_variable:raw} - unencoded output
62710       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
62711       {a_variable:this.method_on_template(...)} - call a method on the template object.
62712  
62713 </code></pre>
62714  *  The tpl tag:
62715 <pre><code>
62716         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
62717         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
62718         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
62719         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
62720   
62721         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
62722         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
62723 </code></pre>
62724  *      
62725  */
62726 Roo.XTemplate = function()
62727 {
62728     Roo.XTemplate.superclass.constructor.apply(this, arguments);
62729     if (this.html) {
62730         this.compile();
62731     }
62732 };
62733
62734
62735 Roo.extend(Roo.XTemplate, Roo.Template, {
62736
62737     /**
62738      * The various sub templates
62739      */
62740     tpls : false,
62741     /**
62742      *
62743      * basic tag replacing syntax
62744      * WORD:WORD()
62745      *
62746      * // you can fake an object call by doing this
62747      *  x.t:(test,tesT) 
62748      * 
62749      */
62750     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
62751
62752     /**
62753      * compile the template
62754      *
62755      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
62756      *
62757      */
62758     compile: function()
62759     {
62760         var s = this.html;
62761      
62762         s = ['<tpl>', s, '</tpl>'].join('');
62763     
62764         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
62765             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
62766             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
62767             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
62768             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
62769             m,
62770             id     = 0,
62771             tpls   = [];
62772     
62773         while(true == !!(m = s.match(re))){
62774             var forMatch   = m[0].match(nameRe),
62775                 ifMatch   = m[0].match(ifRe),
62776                 execMatch   = m[0].match(execRe),
62777                 namedMatch   = m[0].match(namedRe),
62778                 
62779                 exp  = null, 
62780                 fn   = null,
62781                 exec = null,
62782                 name = forMatch && forMatch[1] ? forMatch[1] : '';
62783                 
62784             if (ifMatch) {
62785                 // if - puts fn into test..
62786                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
62787                 if(exp){
62788                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
62789                 }
62790             }
62791             
62792             if (execMatch) {
62793                 // exec - calls a function... returns empty if true is  returned.
62794                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
62795                 if(exp){
62796                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
62797                 }
62798             }
62799             
62800             
62801             if (name) {
62802                 // for = 
62803                 switch(name){
62804                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
62805                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
62806                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
62807                 }
62808             }
62809             var uid = namedMatch ? namedMatch[1] : id;
62810             
62811             
62812             tpls.push({
62813                 id:     namedMatch ? namedMatch[1] : id,
62814                 target: name,
62815                 exec:   exec,
62816                 test:   fn,
62817                 body:   m[1] || ''
62818             });
62819             if (namedMatch) {
62820                 s = s.replace(m[0], '');
62821             } else { 
62822                 s = s.replace(m[0], '{xtpl'+ id + '}');
62823             }
62824             ++id;
62825         }
62826         this.tpls = [];
62827         for(var i = tpls.length-1; i >= 0; --i){
62828             this.compileTpl(tpls[i]);
62829             this.tpls[tpls[i].id] = tpls[i];
62830         }
62831         this.master = tpls[tpls.length-1];
62832         return this;
62833     },
62834     /**
62835      * same as applyTemplate, except it's done to one of the subTemplates
62836      * when using named templates, you can do:
62837      *
62838      * var str = pl.applySubTemplate('your-name', values);
62839      *
62840      * 
62841      * @param {Number} id of the template
62842      * @param {Object} values to apply to template
62843      * @param {Object} parent (normaly the instance of this object)
62844      */
62845     applySubTemplate : function(id, values, parent)
62846     {
62847         
62848         
62849         var t = this.tpls[id];
62850         
62851         
62852         try { 
62853             if(t.test && !t.test.call(this, values, parent)){
62854                 return '';
62855             }
62856         } catch(e) {
62857             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
62858             Roo.log(e.toString());
62859             Roo.log(t.test);
62860             return ''
62861         }
62862         try { 
62863             
62864             if(t.exec && t.exec.call(this, values, parent)){
62865                 return '';
62866             }
62867         } catch(e) {
62868             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
62869             Roo.log(e.toString());
62870             Roo.log(t.exec);
62871             return ''
62872         }
62873         try {
62874             var vs = t.target ? t.target.call(this, values, parent) : values;
62875             parent = t.target ? values : parent;
62876             if(t.target && vs instanceof Array){
62877                 var buf = [];
62878                 for(var i = 0, len = vs.length; i < len; i++){
62879                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
62880                 }
62881                 return buf.join('');
62882             }
62883             return t.compiled.call(this, vs, parent);
62884         } catch (e) {
62885             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
62886             Roo.log(e.toString());
62887             Roo.log(t.compiled);
62888             return '';
62889         }
62890     },
62891
62892     compileTpl : function(tpl)
62893     {
62894         var fm = Roo.util.Format;
62895         var useF = this.disableFormats !== true;
62896         var sep = Roo.isGecko ? "+" : ",";
62897         var undef = function(str) {
62898             Roo.log("Property not found :"  + str);
62899             return '';
62900         };
62901         
62902         var fn = function(m, name, format, args)
62903         {
62904             //Roo.log(arguments);
62905             args = args ? args.replace(/\\'/g,"'") : args;
62906             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
62907             if (typeof(format) == 'undefined') {
62908                 format= 'htmlEncode';
62909             }
62910             if (format == 'raw' ) {
62911                 format = false;
62912             }
62913             
62914             if(name.substr(0, 4) == 'xtpl'){
62915                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
62916             }
62917             
62918             // build an array of options to determine if value is undefined..
62919             
62920             // basically get 'xxxx.yyyy' then do
62921             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
62922             //    (function () { Roo.log("Property not found"); return ''; })() :
62923             //    ......
62924             
62925             var udef_ar = [];
62926             var lookfor = '';
62927             Roo.each(name.split('.'), function(st) {
62928                 lookfor += (lookfor.length ? '.': '') + st;
62929                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
62930             });
62931             
62932             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
62933             
62934             
62935             if(format && useF){
62936                 
62937                 args = args ? ',' + args : "";
62938                  
62939                 if(format.substr(0, 5) != "this."){
62940                     format = "fm." + format + '(';
62941                 }else{
62942                     format = 'this.call("'+ format.substr(5) + '", ';
62943                     args = ", values";
62944                 }
62945                 
62946                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
62947             }
62948              
62949             if (args.length) {
62950                 // called with xxyx.yuu:(test,test)
62951                 // change to ()
62952                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
62953             }
62954             // raw.. - :raw modifier..
62955             return "'"+ sep + udef_st  + name + ")"+sep+"'";
62956             
62957         };
62958         var body;
62959         // branched to use + in gecko and [].join() in others
62960         if(Roo.isGecko){
62961             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
62962                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
62963                     "';};};";
62964         }else{
62965             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
62966             body.push(tpl.body.replace(/(\r\n|\n)/g,
62967                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
62968             body.push("'].join('');};};");
62969             body = body.join('');
62970         }
62971         
62972         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
62973        
62974         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
62975         eval(body);
62976         
62977         return this;
62978     },
62979
62980     applyTemplate : function(values){
62981         return this.master.compiled.call(this, values, {});
62982         //var s = this.subs;
62983     },
62984
62985     apply : function(){
62986         return this.applyTemplate.apply(this, arguments);
62987     }
62988
62989  });
62990
62991 Roo.XTemplate.from = function(el){
62992     el = Roo.getDom(el);
62993     return new Roo.XTemplate(el.value || el.innerHTML);
62994 };